linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/11] selftests: hid: import the tests from hid-tools
@ 2023-02-17 16:17 Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 01/11] selftests: hid: make vmtest rely on make Benjamin Tissoires
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Peter Hutterer, Candle Sun, Jose Torreguitar,
	Roderick Colenbrander, Silvan Jegen, Kai-Heng Feng,
	наб, Blaž Hrastnik, Jason Gerecke,
	Nicolas Saenz Julienne

I have been running hid-tools for a while, but it was in its own
separate repository for multiple reasons. And the past few weeks
I finally managed to make the kernel tests in that repo in a
state where we can merge them in the kernel tree directly:

- the tests run in ~2 to 3 minutes
- the tests are way more reliable than previously
- the tests are mostly self-contained now (to the exception
  of the Sony ones)

To be able to run the tests we need to use the latest release
of hid-tools, as this project still keeps the HID parsing logic
and is capable of generating the HID events.

The series also ensures we can run the tests with vmtest.sh,
allowing for a quick development and test in the tree itself.

This should allow us to require tests to be added to a series
when we see fit and keep them alive properly instead of having
to deal with 2 repositories.

In Cc are all of the people who participated in the elaboration
of those tests, so please send back a signed-off-by for each
commit you are part of.

This series applies on top of the for-6.3/hid-bpf branch, which
is the one that added the tools/testing/selftests/hid directory.
Given that this is unlikely this series will make the cut for
6.3, we might just consider this series to be based on top of
the future 6.3-rc1.

Cheers,
Benjamin

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
Benjamin Tissoires (11):
      selftests: hid: make vmtest rely on make
      selftests: hid: import hid-tools hid-core tests
      selftests: hid: import hid-tools hid-gamepad tests
      selftests: hid: import hid-tools hid-keyboards tests
      selftests: hid: import hid-tools hid-mouse tests
      selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
      selftests: hid: import hid-tools wacom tests
      selftests: hid: import hid-tools hid-apple tests
      selftests: hid: import hid-tools hid-ite tests
      selftests: hid: import hid-tools hid-sony and hid-playstation tests
      selftests: hid: import hid-tools usb-crash tests

 tools/testing/selftests/hid/Makefile               |   12 +
 tools/testing/selftests/hid/config                 |   11 +
 tools/testing/selftests/hid/hid-apple.sh           |    7 +
 tools/testing/selftests/hid/hid-core.sh            |    7 +
 tools/testing/selftests/hid/hid-gamepad.sh         |    7 +
 tools/testing/selftests/hid/hid-ite.sh             |    7 +
 tools/testing/selftests/hid/hid-keyboard.sh        |    7 +
 tools/testing/selftests/hid/hid-mouse.sh           |    7 +
 tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
 tools/testing/selftests/hid/hid-sony.sh            |    7 +
 tools/testing/selftests/hid/hid-tablet.sh          |    7 +
 tools/testing/selftests/hid/hid-usb_crash.sh       |    7 +
 tools/testing/selftests/hid/hid-wacom.sh           |    7 +
 tools/testing/selftests/hid/run-hid-tools-tests.sh |   28 +
 tools/testing/selftests/hid/settings               |    3 +
 tools/testing/selftests/hid/tests/__init__.py      |    2 +
 tools/testing/selftests/hid/tests/base.py          |  345 ++++
 tools/testing/selftests/hid/tests/conftest.py      |   81 +
 .../selftests/hid/tests/descriptors_wacom.py       | 1360 +++++++++++++
 .../selftests/hid/tests/test_apple_keyboard.py     |  440 +++++
 tools/testing/selftests/hid/tests/test_gamepad.py  |  209 ++
 tools/testing/selftests/hid/tests/test_hid_core.py |  154 ++
 .../selftests/hid/tests/test_ite_keyboard.py       |  166 ++
 tools/testing/selftests/hid/tests/test_keyboard.py |  485 +++++
 tools/testing/selftests/hid/tests/test_mouse.py    |  977 +++++++++
 .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
 tools/testing/selftests/hid/tests/test_sony.py     |  282 +++
 tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
 .../testing/selftests/hid/tests/test_usb_crash.py  |  103 +
 .../selftests/hid/tests/test_wacom_generic.py      |  844 ++++++++
 tools/testing/selftests/hid/vmtest.sh              |   25 +-
 31 files changed, 8554 insertions(+), 10 deletions(-)
---
base-commit: 2f7f4efb9411770b4ad99eb314d6418e980248b4
change-id: 20230217-import-hid-tools-tests-dc0cd4f3c8a8

Best regards,
-- 
Benjamin Tissoires <benjamin.tissoires@redhat.com>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 01/11] selftests: hid: make vmtest rely on make
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
@ 2023-02-17 16:17 ` Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 02/11] selftests: hid: import hid-tools hid-core tests Benjamin Tissoires
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires

Having a default binary is simple enough, but this also means that
we need to keep the targets in sync as we are adding them in the Makefile.

So instead of doing that manual work, make vmtest.sh generic enough to
actually be capable of running 'make -C tools/testing/selftests/hid'.

The new image we use has make installed, which the base fedora image
doesn't.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/vmtest.sh | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh
index 90f34150f257..6346b0620dba 100755
--- a/tools/testing/selftests/hid/vmtest.sh
+++ b/tools/testing/selftests/hid/vmtest.sh
@@ -16,7 +16,6 @@ x86_64)
 	exit 1
 	;;
 esac
-DEFAULT_COMMAND="./hid_bpf"
 SCRIPT_DIR="$(dirname $(realpath $0))"
 OUTPUT_DIR="$SCRIPT_DIR/results"
 KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}")
@@ -25,7 +24,10 @@ NUM_COMPILE_JOBS="$(nproc)"
 LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")"
 LOG_FILE="${LOG_FILE_BASE}.log"
 EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
-CONTAINER_IMAGE="registry.fedoraproject.org/fedora:36"
+CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1"
+
+TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}"
+DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
 
 usage()
 {
@@ -33,9 +35,9 @@ usage()
 Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
 
 <command> is the command you would normally run when you are in
-tools/testing/selftests/bpf. e.g:
+the source kernel direcory. e.g:
 
-	$0 -- ./hid_bpf
+	$0 -- ./tools/testing/selftests/hid/hid_bpf
 
 If no command is specified and a debug shell (-s) is not requested,
 "${DEFAULT_COMMAND}" will be run by default.
@@ -43,11 +45,11 @@ If no command is specified and a debug shell (-s) is not requested,
 If you build your kernel using KBUILD_OUTPUT= or O= options, these
 can be passed as environment variables to the script:
 
-  O=<kernel_build_path> $0 -- ./hid_bpf
+  O=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf
 
 or
 
-  KBUILD_OUTPUT=<kernel_build_path> $0 -- ./hid_bpf
+  KBUILD_OUTPUT=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf
 
 Options:
 
@@ -91,11 +93,14 @@ update_selftests()
 
 run_vm()
 {
-	local b2c="$1"
-	local kernel_bzimage="$2"
-	local command="$3"
+	local run_dir="$1"
+	local b2c="$2"
+	local kernel_bzimage="$3"
+	local command="$4"
 	local post_command=""
 
+	cd "${run_dir}"
+
 	if ! which "${QEMU_BINARY}" &> /dev/null; then
 		cat <<EOF
 Could not find ${QEMU_BINARY}
@@ -273,7 +278,7 @@ main()
 	fi
 
 	update_selftests "${kernel_checkout}" "${make_command}"
-	run_vm $b2c "${kernel_bzimage}" "${command}"
+	run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
 	if [[ "${debug_shell}" != "yes" ]]; then
 		echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
 	fi

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 02/11] selftests: hid: import hid-tools hid-core tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 01/11] selftests: hid: make vmtest rely on make Benjamin Tissoires
@ 2023-02-17 16:17 ` Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests Benjamin Tissoires
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Peter Hutterer

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.

I haven't imported all of hid-tools, the python module, but only the
tests related to the kernel. We can rely on pip to fetch the latest
hid-tools release, and then run the tests directly from the tree.

This should now be easier to request tests when something is not behaving
properly in the HID subsystem.

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

Cc: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile               |   2 +
 tools/testing/selftests/hid/hid-core.sh            |   7 +
 tools/testing/selftests/hid/run-hid-tools-tests.sh |  28 ++
 tools/testing/selftests/hid/tests/__init__.py      |   2 +
 tools/testing/selftests/hid/tests/base.py          | 345 +++++++++++++++++++++
 tools/testing/selftests/hid/tests/conftest.py      |  81 +++++
 tools/testing/selftests/hid/tests/test_hid_core.py | 154 +++++++++
 tools/testing/selftests/hid/vmtest.sh              |   2 +-
 8 files changed, 620 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 83e8f87d643a..bdcb36d80c8c 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -5,6 +5,8 @@ include ../../../build/Build.include
 include ../../../scripts/Makefile.arch
 include ../../../scripts/Makefile.include
 
+TEST_PROGS := hid-core.sh
+
 CXX ?= $(CROSS_COMPILE)g++
 
 HOSTPKG_CONFIG := pkg-config
diff --git a/tools/testing/selftests/hid/hid-core.sh b/tools/testing/selftests/hid/hid-core.sh
new file mode 100755
index 000000000000..5bbabc12c34f
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-core.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_hid_core.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/run-hid-tools-tests.sh b/tools/testing/selftests/hid/run-hid-tools-tests.sh
new file mode 100755
index 000000000000..bdae8464da86
--- /dev/null
+++ b/tools/testing/selftests/hid/run-hid-tools-tests.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+if ! command -v python3 > /dev/null 2>&1; then
+	echo "hid-tools: [SKIP] python3 not installed"
+	exit 77
+fi
+
+if ! python3 -c "import pytest" > /dev/null 2>&1; then
+	echo "hid: [SKIP/ pytest module not installed"
+	exit 77
+fi
+
+if ! python3 -c "import pytest_tap" > /dev/null 2>&1; then
+	echo "hid: [SKIP/ pytest_tap module not installed"
+	exit 77
+fi
+
+if ! python3 -c "import hidtools" > /dev/null 2>&1; then
+	echo "hid: [SKIP/ hid-tools module not installed"
+	exit 77
+fi
+
+TARGET=${TARGET:=.}
+
+echo TAP version 13
+python3 -u -m pytest $PYTEST_XDIST ./tests/$TARGET --tap-stream --udevd
diff --git a/tools/testing/selftests/hid/tests/__init__.py b/tools/testing/selftests/hid/tests/__init__.py
new file mode 100644
index 000000000000..c940e9275252
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/__init__.py
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# Just to make sphinx-apidoc document this directory
diff --git a/tools/testing/selftests/hid/tests/base.py b/tools/testing/selftests/hid/tests/base.py
new file mode 100644
index 000000000000..1305cfc9646e
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/base.py
@@ -0,0 +1,345 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2017 Red Hat, Inc.
+
+import libevdev
+import os
+import pytest
+import time
+
+import logging
+
+from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile
+from pathlib import Path
+from typing import Final
+
+logger = logging.getLogger("hidtools.test.base")
+
+# application to matches
+application_matches: Final = {
+    # pyright: ignore
+    "Accelerometer": EvdevMatch(
+        req_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ]
+    ),
+    "Game Pad": EvdevMatch(  # in systemd, this is a lot more complex, but that will do
+        requires=[
+            libevdev.EV_ABS.ABS_X,
+            libevdev.EV_ABS.ABS_Y,
+            libevdev.EV_ABS.ABS_RX,
+            libevdev.EV_ABS.ABS_RY,
+            libevdev.EV_KEY.BTN_START,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Joystick": EvdevMatch(  # in systemd, this is a lot more complex, but that will do
+        requires=[
+            libevdev.EV_ABS.ABS_RX,
+            libevdev.EV_ABS.ABS_RY,
+            libevdev.EV_KEY.BTN_START,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Key": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.KEY_A,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+            libevdev.INPUT_PROP_DIRECT,
+            libevdev.INPUT_PROP_POINTER,
+        ],
+    ),
+    "Mouse": EvdevMatch(
+        requires=[
+            libevdev.EV_REL.REL_X,
+            libevdev.EV_REL.REL_Y,
+            libevdev.EV_KEY.BTN_LEFT,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Pad": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.BTN_0,
+        ],
+        excludes=[
+            libevdev.EV_KEY.BTN_TOOL_PEN,
+            libevdev.EV_KEY.BTN_TOUCH,
+            libevdev.EV_ABS.ABS_DISTANCE,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Pen": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.BTN_STYLUS,
+            libevdev.EV_ABS.ABS_X,
+            libevdev.EV_ABS.ABS_Y,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Stylus": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.BTN_STYLUS,
+            libevdev.EV_ABS.ABS_X,
+            libevdev.EV_ABS.ABS_Y,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Touch Pad": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.BTN_LEFT,
+            libevdev.EV_ABS.ABS_X,
+            libevdev.EV_ABS.ABS_Y,
+        ],
+        excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS],
+        req_properties=[
+            libevdev.INPUT_PROP_POINTER,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+    "Touch Screen": EvdevMatch(
+        requires=[
+            libevdev.EV_KEY.BTN_TOUCH,
+            libevdev.EV_ABS.ABS_X,
+            libevdev.EV_ABS.ABS_Y,
+        ],
+        excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS],
+        req_properties=[
+            libevdev.INPUT_PROP_DIRECT,
+        ],
+        excl_properties=[
+            libevdev.INPUT_PROP_ACCELEROMETER,
+        ],
+    ),
+}
+
+
+class UHIDTestDevice(BaseDevice):
+    def __init__(self, name, application, rdesc_str=None, rdesc=None, input_info=None):
+        super().__init__(name, application, rdesc_str, rdesc, input_info)
+        self.application_matches = application_matches
+        if name is None:
+            name = f"uhid test {self.__class__.__name__}"
+        if not name.startswith("uhid test "):
+            name = "uhid test " + self.name
+        self.name = name
+
+
+class BaseTestCase:
+    class TestUhid(object):
+        syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT)  # type: ignore
+        key_event = libevdev.InputEvent(libevdev.EV_KEY)  # type: ignore
+        abs_event = libevdev.InputEvent(libevdev.EV_ABS)  # type: ignore
+        rel_event = libevdev.InputEvent(libevdev.EV_REL)  # type: ignore
+        msc_event = libevdev.InputEvent(libevdev.EV_MSC.MSC_SCAN)  # type: ignore
+
+        # 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 = []
+
+        def assertInputEventsIn(self, expected_events, effective_events):
+            effective_events = effective_events.copy()
+            for ev in expected_events:
+                assert ev in effective_events
+                effective_events.remove(ev)
+            return effective_events
+
+        def assertInputEvents(self, expected_events, effective_events):
+            remaining = self.assertInputEventsIn(expected_events, effective_events)
+            assert remaining == []
+
+        @classmethod
+        def debug_reports(cls, reports, uhdev=None, events=None):
+            data = [" ".join([f"{v:02x}" for v in r]) for r in reports]
+
+            if uhdev is not None:
+                human_data = [
+                    uhdev.parsed_rdesc.format_report(r, split_lines=True)
+                    for r in reports
+                ]
+                try:
+                    human_data = [
+                        f'\n\t       {" " * h.index("/")}'.join(h.split("\n"))
+                        for h in human_data
+                    ]
+                except ValueError:
+                    # '/' not found: not a numbered report
+                    human_data = ["\n\t      ".join(h.split("\n")) for h in human_data]
+                data = [f"{d}\n\t ====> {h}" for d, h in zip(data, human_data)]
+
+            reports = data
+
+            if len(reports) == 1:
+                print("sending 1 report:")
+            else:
+                print(f"sending {len(reports)} reports:")
+            for report in reports:
+                print("\t", report)
+
+            if events is not None:
+                print("events received:", events)
+
+        def create_device(self):
+            raise Exception("please reimplement me in subclasses")
+
+        def _load_kernel_module(self, kernel_driver, kernel_module):
+            sysfs_path = Path("/sys/bus/hid/drivers")
+            if kernel_driver is not None:
+                sysfs_path /= kernel_driver
+            else:
+                # special case for when testing all available modules:
+                # we don't know beforehand the name of the module from modinfo
+                sysfs_path = Path("/sys/module") / kernel_module.replace("-", "_")
+            if not sysfs_path.exists():
+                import subprocess
+
+                ret = subprocess.run(["/usr/sbin/modprobe", kernel_module])
+                if ret.returncode != 0:
+                    pytest.skip(
+                        f"module {kernel_module} could not be loaded, skipping the test"
+                    )
+
+        @pytest.fixture()
+        def load_kernel_module(self):
+            for kernel_driver, kernel_module in self.kernel_modules:
+                self._load_kernel_module(kernel_driver, kernel_module)
+            yield
+
+        @pytest.fixture()
+        def new_uhdev(self, load_kernel_module):
+            return self.create_device()
+
+        def assertName(self, uhdev):
+            evdev = uhdev.get_evdev()
+            assert uhdev.name in evdev.name
+
+        @pytest.fixture(autouse=True)
+        def context(self, new_uhdev, request):
+            try:
+                with HIDTestUdevRule.instance():
+                    with new_uhdev as self.uhdev:
+                        skip_cond = request.node.get_closest_marker("skip_if_uhdev")
+                        if skip_cond:
+                            test, message, *rest = skip_cond.args
+
+                            if test(self.uhdev):
+                                pytest.skip(message)
+
+                        self.uhdev.create_kernel_device()
+                        now = time.time()
+                        while not self.uhdev.is_ready() and time.time() - now < 5:
+                            self.uhdev.dispatch(1)
+                        if self.uhdev.get_evdev() is None:
+                            logger.warning(
+                                f"available list of input nodes: (default application is '{self.uhdev.application}')"
+                            )
+                            logger.warning(self.uhdev.input_nodes)
+                        yield
+                        self.uhdev = None
+            except PermissionError:
+                pytest.skip("Insufficient permissions, run me as root")
+
+        @pytest.fixture(autouse=True)
+        def check_taint(self):
+            # we are abusing SysfsFile here, it's in /proc, but meh
+            taint_file = SysfsFile("/proc/sys/kernel/tainted")
+            taint = taint_file.int_value
+
+            yield
+
+            assert taint_file.int_value == taint
+
+        def test_creation(self):
+            """Make sure the device gets processed by the kernel and creates
+            the expected application input node.
+
+            If this fail, there is something wrong in the device report
+            descriptors."""
+            uhdev = self.uhdev
+            assert uhdev is not None
+            assert uhdev.get_evdev() is not None
+            self.assertName(uhdev)
+            assert len(uhdev.next_sync_events()) == 0
+            assert uhdev.get_evdev() is not None
+
+
+class HIDTestUdevRule(object):
+    _instance = None
+    """
+    A context-manager compatible class that sets up our udev rules file and
+    deletes it on context exit.
+
+    This class is tailored to our test setup: it only sets up the udev rule
+    on the **second** context and it cleans it up again on the last context
+    removed. This matches the expected pytest setup: we enter a context for
+    the session once, then once for each test (the first of which will
+    trigger the udev rule) and once the last test exited and the session
+    exited, we clean up after ourselves.
+    """
+
+    def __init__(self):
+        self.refs = 0
+        self.rulesfile = None
+
+    def __enter__(self):
+        self.refs += 1
+        if self.refs == 2 and self.rulesfile is None:
+            self.create_udev_rule()
+            self.reload_udev_rules()
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.refs -= 1
+        if self.refs == 0 and self.rulesfile:
+            os.remove(self.rulesfile.name)
+            self.reload_udev_rules()
+
+    def reload_udev_rules(self):
+        import subprocess
+
+        subprocess.run("udevadm control --reload-rules".split())
+        subprocess.run("systemd-hwdb update".split())
+
+    def create_udev_rule(self):
+        import tempfile
+
+        os.makedirs("/run/udev/rules.d", exist_ok=True)
+        with tempfile.NamedTemporaryFile(
+            prefix="91-uhid-test-device-REMOVEME-",
+            suffix=".rules",
+            mode="w+",
+            dir="/run/udev/rules.d",
+            delete=False,
+        ) as f:
+            f.write(
+                'KERNELS=="*input*", ATTRS{name}=="*uhid test *", ENV{LIBINPUT_IGNORE_DEVICE}="1"\n'
+            )
+            f.write(
+                'KERNELS=="*input*", ATTRS{name}=="*uhid test * System Multi Axis", ENV{ID_INPUT_TOUCHSCREEN}="", ENV{ID_INPUT_SYSTEM_MULTIAXIS}="1"\n'
+            )
+            self.rulesfile = f
+
+    @classmethod
+    def instance(cls):
+        if not cls._instance:
+            cls._instance = HIDTestUdevRule()
+        return cls._instance
diff --git a/tools/testing/selftests/hid/tests/conftest.py b/tools/testing/selftests/hid/tests/conftest.py
new file mode 100644
index 000000000000..1361ec981db6
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/conftest.py
@@ -0,0 +1,81 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2017 Red Hat, Inc.
+
+import platform
+import pytest
+import re
+import resource
+import subprocess
+from .base import HIDTestUdevRule
+from pathlib import Path
+
+
+# See the comment in HIDTestUdevRule, this doesn't set up but it will clean
+# up once the last test exited.
+@pytest.fixture(autouse=True, scope="session")
+def udev_rules_session_setup():
+    with HIDTestUdevRule.instance():
+        yield
+
+
+@pytest.fixture(autouse=True, scope="session")
+def setup_rlimit():
+    resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
+
+
+@pytest.fixture(autouse=True, scope="session")
+def start_udevd(pytestconfig):
+    if pytestconfig.getoption("udevd"):
+        import subprocess
+
+        with subprocess.Popen("/usr/lib/systemd/systemd-udevd") as proc:
+            yield
+            proc.kill()
+    else:
+        yield
+
+
+def pytest_configure(config):
+    config.addinivalue_line(
+        "markers",
+        "skip_if_uhdev(condition, message): mark test to skip if the condition on the uhdev device is met",
+    )
+
+
+# Generate the list of modules and modaliases
+# for the tests that need to be parametrized with those
+def pytest_generate_tests(metafunc):
+    if "usbVidPid" in metafunc.fixturenames:
+        modules = (
+            Path("/lib/modules/")
+            / platform.uname().release
+            / "kernel"
+            / "drivers"
+            / "hid"
+        )
+
+        modalias_re = re.compile(r"alias:\s+hid:b0003g.*v([0-9a-fA-F]+)p([0-9a-fA-F]+)")
+
+        params = []
+        ids = []
+        for module in modules.glob("*.ko"):
+            p = subprocess.run(
+                ["modinfo", module], capture_output=True, check=True, encoding="utf-8"
+            )
+            for line in p.stdout.split("\n"):
+                m = modalias_re.match(line)
+                if m is not None:
+                    vid, pid = m.groups()
+                    vid = int(vid, 16)
+                    pid = int(pid, 16)
+                    params.append([module.name.replace(".ko", ""), vid, pid])
+                    ids.append(f"{module.name} {vid:04x}:{pid:04x}")
+        metafunc.parametrize("usbVidPid", params, ids=ids)
+
+
+def pytest_addoption(parser):
+    parser.addoption("--udevd", action="store_true", default=False)
diff --git a/tools/testing/selftests/hid/tests/test_hid_core.py b/tools/testing/selftests/hid/tests/test_hid_core.py
new file mode 100644
index 000000000000..9a7fe40020d2
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_hid_core.py
@@ -0,0 +1,154 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2017 Red Hat, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# This is for generic devices
+
+from . import base
+import logging
+
+logger = logging.getLogger("hidtools.test.hid")
+
+
+class TestCollectionOverflow(base.BaseTestCase.TestUhid):
+    """
+    Test class to test re-allocation of the HID collection stack in
+    hid-core.c.
+    """
+
+    def create_device(self):
+        # fmt: off
+        report_descriptor = [
+            0x05, 0x01,         # .Usage Page (Generic Desktop)
+            0x09, 0x02,         # .Usage (Mouse)
+            0xa1, 0x01,         # .Collection (Application)
+            0x09, 0x02,         # ..Usage (Mouse)
+            0xa1, 0x02,         # ..Collection (Logical)
+            0x09, 0x01,         # ...Usage (Pointer)
+            0xa1, 0x00,         # ...Collection (Physical)
+            0x05, 0x09,         # ....Usage Page (Button)
+            0x19, 0x01,         # ....Usage Minimum (1)
+            0x29, 0x03,         # ....Usage Maximum (3)
+            0x15, 0x00,         # ....Logical Minimum (0)
+            0x25, 0x01,         # ....Logical Maximum (1)
+            0x75, 0x01,         # ....Report Size (1)
+            0x95, 0x03,         # ....Report Count (3)
+            0x81, 0x02,         # ....Input (Data,Var,Abs)
+            0x75, 0x05,         # ....Report Size (5)
+            0x95, 0x01,         # ....Report Count (1)
+            0x81, 0x03,         # ....Input (Cnst,Var,Abs)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0xa1, 0x02,         # ....Collection (Logical)
+            0x09, 0x01,         # .....Usage (Pointer)
+            0x05, 0x01,         # .....Usage Page (Generic Desktop)
+            0x09, 0x30,         # .....Usage (X)
+            0x09, 0x31,         # .....Usage (Y)
+            0x15, 0x81,         # .....Logical Minimum (-127)
+            0x25, 0x7f,         # .....Logical Maximum (127)
+            0x75, 0x08,         # .....Report Size (8)
+            0x95, 0x02,         # .....Report Count (2)
+            0x81, 0x06,         # .....Input (Data,Var,Rel)
+            0xa1, 0x02,         # ...Collection (Logical)
+            0x85, 0x12,         # ....Report ID (18)
+            0x09, 0x48,         # ....Usage (Resolution Multiplier)
+            0x95, 0x01,         # ....Report Count (1)
+            0x75, 0x02,         # ....Report Size (2)
+            0x15, 0x00,         # ....Logical Minimum (0)
+            0x25, 0x01,         # ....Logical Maximum (1)
+            0x35, 0x01,         # ....Physical Minimum (1)
+            0x45, 0x0c,         # ....Physical Maximum (12)
+            0xb1, 0x02,         # ....Feature (Data,Var,Abs)
+            0x85, 0x1a,         # ....Report ID (26)
+            0x09, 0x38,         # ....Usage (Wheel)
+            0x35, 0x00,         # ....Physical Minimum (0)
+            0x45, 0x00,         # ....Physical Maximum (0)
+            0x95, 0x01,         # ....Report Count (1)
+            0x75, 0x10,         # ....Report Size (16)
+            0x16, 0x01, 0x80,   # ....Logical Minimum (-32767)
+            0x26, 0xff, 0x7f,   # ....Logical Maximum (32767)
+            0x81, 0x06,         # ....Input (Data,Var,Rel)
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ...End Collection
+            0xc0,               # ..End Collection
+            0xc0,               # .End Collection
+        ]
+        # fmt: on
+        return base.UHIDTestDevice(
+            name=None, rdesc=report_descriptor, application="Mouse"
+        )
+
+    def test_rdesc(self):
+        """
+        This test can only check for negatives. If the kernel crashes, you
+        know why. If this test passes, either the bug isn't present or just
+        didn't get triggered. No way to know.
+
+        For an explanation, see kernel patch
+            HID: core: replace the collection tree pointers with indices
+        """
+        pass
diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh
index 6346b0620dba..681b906b4853 100755
--- a/tools/testing/selftests/hid/vmtest.sh
+++ b/tools/testing/selftests/hid/vmtest.sh
@@ -27,7 +27,7 @@ EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
 CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1"
 
 TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}"
-DEFAULT_COMMAND="make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
+DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
 
 usage()
 {

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 01/11] selftests: hid: make vmtest rely on make Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 02/11] selftests: hid: import hid-tools hid-core tests Benjamin Tissoires
@ 2023-02-17 16:17 ` Benjamin Tissoires
  2023-02-18 20:24   ` Silvan Jegen
  2023-02-17 16:17 ` [PATCH 04/11] selftests: hid: import hid-tools hid-keyboards tests Benjamin Tissoires
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Candle Sun, Jose Torreguitar, Peter Hutterer,
	Roderick Colenbrander, Silvan Jegen

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: Candle Sun <candle.sun@unisoc.com>
Cc: Jose Torreguitar <jtguitar@google.com>
Cc: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
Cc: Silvan Jegen <s.jegen@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile              |   1 +
 tools/testing/selftests/hid/hid-gamepad.sh        |   7 +
 tools/testing/selftests/hid/tests/test_gamepad.py | 209 ++++++++++++++++++++++
 3 files changed, 217 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index bdcb36d80c8c..d16a22477140 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch
 include ../../../scripts/Makefile.include
 
 TEST_PROGS := hid-core.sh
+TEST_PROGS += hid-gamepad.sh
 
 CXX ?= $(CROSS_COMPILE)g++
 
diff --git a/tools/testing/selftests/hid/hid-gamepad.sh b/tools/testing/selftests/hid/hid-gamepad.sh
new file mode 100755
index 000000000000..1ba00c0ca95f
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-gamepad.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_gamepad.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py
new file mode 100644
index 000000000000..26c74040b796
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_gamepad.py
@@ -0,0 +1,209 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2019 Red Hat, Inc.
+#
+
+from . import base
+import libevdev
+import pytest
+
+from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad
+
+import logging
+
+logger = logging.getLogger("hidtools.test.gamepad")
+
+
+class BaseTest:
+    class TestGamepad(base.BaseTestCase.TestUhid):
+        @pytest.fixture(autouse=True)
+        def send_initial_state(self):
+            """send an empty report to initialize the axes"""
+            uhdev = self.uhdev
+
+            r = uhdev.event()
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+
+        def assert_button(self, button):
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev()
+            syn_event = self.syn_event
+
+            buttons = {}
+            key = libevdev.evbit(uhdev.buttons_map[button])
+
+            buttons[button] = True
+            r = uhdev.event(buttons=buttons)
+            expected_event = libevdev.InputEvent(key, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[key] == 1
+
+            buttons[button] = False
+            r = uhdev.event(buttons=buttons)
+            expected_event = libevdev.InputEvent(key, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[key] == 0
+
+        def test_buttons(self):
+            """check for button reliability."""
+            uhdev = self.uhdev
+
+            for b in uhdev.buttons:
+                self.assert_button(b)
+
+        def test_dual_buttons(self):
+            """check for button reliability when pressing 2 buttons"""
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev()
+            syn_event = self.syn_event
+
+            # can change intended b1 b2 values
+            b1 = uhdev.buttons[0]
+            key1 = libevdev.evbit(uhdev.buttons_map[b1])
+            b2 = uhdev.buttons[1]
+            key2 = libevdev.evbit(uhdev.buttons_map[b2])
+
+            buttons = {b1: True, b2: True}
+            r = uhdev.event(buttons=buttons)
+            expected_event0 = libevdev.InputEvent(key1, 1)
+            expected_event1 = libevdev.InputEvent(key2, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(
+                (syn_event, expected_event0, expected_event1), events
+            )
+            assert evdev.value[key1] == 1
+            assert evdev.value[key2] == 1
+
+            buttons = {b1: False, b2: None}
+            r = uhdev.event(buttons=buttons)
+            expected_event = libevdev.InputEvent(key1, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[key1] == 0
+            assert evdev.value[key2] == 1
+
+            buttons = {b1: None, b2: False}
+            r = uhdev.event(buttons=buttons)
+            expected_event = libevdev.InputEvent(key2, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[key1] == 0
+            assert evdev.value[key2] == 0
+
+        def _get_libevdev_abs_events(self, which):
+            """Returns which ABS_* evdev axes are expected for the given stick"""
+            abs_map = self.uhdev.axes_map[which]
+
+            x = abs_map["x"].evdev
+            y = abs_map["y"].evdev
+
+            assert x
+            assert y
+
+            return x, y
+
+        def _test_joystick_press(self, which, data):
+            uhdev = self.uhdev
+
+            libevdev_axes = self._get_libevdev_abs_events(which)
+
+            r = None
+            if which == "left_stick":
+                r = uhdev.event(left=data)
+            else:
+                r = uhdev.event(right=data)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+
+            for i, d in enumerate(data):
+                if d is not None and d != 127:
+                    assert libevdev.InputEvent(libevdev_axes[i], d) in events
+                else:
+                    assert libevdev.InputEvent(libevdev_axes[i]) not in events
+
+        def test_left_joystick_press_left(self):
+            """check for the left joystick reliability"""
+            self._test_joystick_press("left_stick", (63, None))
+            self._test_joystick_press("left_stick", (0, 127))
+
+        def test_left_joystick_press_right(self):
+            """check for the left joystick reliability"""
+            self._test_joystick_press("left_stick", (191, 127))
+            self._test_joystick_press("left_stick", (255, None))
+
+        def test_left_joystick_press_up(self):
+            """check for the left joystick reliability"""
+            self._test_joystick_press("left_stick", (None, 63))
+            self._test_joystick_press("left_stick", (127, 0))
+
+        def test_left_joystick_press_down(self):
+            """check for the left joystick reliability"""
+            self._test_joystick_press("left_stick", (127, 191))
+            self._test_joystick_press("left_stick", (None, 255))
+
+        def test_right_joystick_press_left(self):
+            """check for the right joystick reliability"""
+            self._test_joystick_press("right_stick", (63, None))
+            self._test_joystick_press("right_stick", (0, 127))
+
+        def test_right_joystick_press_right(self):
+            """check for the right joystick reliability"""
+            self._test_joystick_press("right_stick", (191, 127))
+            self._test_joystick_press("right_stick", (255, None))
+
+        def test_right_joystick_press_up(self):
+            """check for the right joystick reliability"""
+            self._test_joystick_press("right_stick", (None, 63))
+            self._test_joystick_press("right_stick", (127, 0))
+
+        def test_right_joystick_press_down(self):
+            """check for the right joystick reliability"""
+            self._test_joystick_press("right_stick", (127, 191))
+            self._test_joystick_press("right_stick", (None, 255))
+
+        @pytest.mark.skip_if_uhdev(
+            lambda uhdev: "Hat switch" not in uhdev.fields,
+            "Device not compatible, missing Hat switch usage",
+        )
+        @pytest.mark.parametrize(
+            "hat_value,expected_evdev,evdev_value",
+            [
+                (0, "ABS_HAT0Y", -1),
+                (2, "ABS_HAT0X", 1),
+                (4, "ABS_HAT0Y", 1),
+                (6, "ABS_HAT0X", -1),
+            ],
+        )
+        def test_hat_switch(self, hat_value, expected_evdev, evdev_value):
+            uhdev = self.uhdev
+
+            r = uhdev.event(hat_switch=hat_value)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            assert (
+                libevdev.InputEvent(
+                    libevdev.evbit("EV_ABS", expected_evdev), evdev_value
+                )
+                in events
+            )
+
+
+class TestSaitekGamepad(BaseTest.TestGamepad):
+    def create_device(self):
+        return SaitekGamepad()
+
+
+class TestAsusGamepad(BaseTest.TestGamepad):
+    def create_device(self):
+        return AsusGamepad()

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 04/11] selftests: hid: import hid-tools hid-keyboards tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (2 preceding siblings ...)
  2023-02-17 16:17 ` [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests Benjamin Tissoires
@ 2023-02-17 16:17 ` Benjamin Tissoires
  2023-02-17 16:17 ` [PATCH 05/11] selftests: hid: import hid-tools hid-mouse tests Benjamin Tissoires
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Peter Hutterer, Roderick Colenbrander, Nicolas Saenz Julienne

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: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile               |   1 +
 tools/testing/selftests/hid/hid-keyboard.sh        |   7 +
 tools/testing/selftests/hid/tests/test_keyboard.py | 485 +++++++++++++++++++++
 3 files changed, 493 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index d16a22477140..181a594ffe92 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -7,6 +7,7 @@ include ../../../scripts/Makefile.include
 
 TEST_PROGS := hid-core.sh
 TEST_PROGS += hid-gamepad.sh
+TEST_PROGS += hid-keyboard.sh
 
 CXX ?= $(CROSS_COMPILE)g++
 
diff --git a/tools/testing/selftests/hid/hid-keyboard.sh b/tools/testing/selftests/hid/hid-keyboard.sh
new file mode 100755
index 000000000000..55368f17d1d5
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-keyboard.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_keyboard.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_keyboard.py b/tools/testing/selftests/hid/tests/test_keyboard.py
new file mode 100644
index 000000000000..b3b2bdbf63b7
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_keyboard.py
@@ -0,0 +1,485 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2018 Red Hat, Inc.
+#
+
+from . import base
+import hidtools.hid
+import libevdev
+import logging
+
+logger = logging.getLogger("hidtools.test.keyboard")
+
+
+class InvalidHIDCommunication(Exception):
+    pass
+
+
+class KeyboardData(object):
+    pass
+
+
+class BaseKeyboard(base.UHIDTestDevice):
+    def __init__(self, rdesc, name=None, input_info=None):
+        assert rdesc is not None
+        super().__init__(name, "Key", input_info=input_info, rdesc=rdesc)
+        self.keystates = {}
+
+    def _update_key_state(self, keys):
+        """
+        Update the internal state of keys with the new state given.
+
+        :param key: a tuple of chars for the currently pressed keys.
+        """
+        # First remove the already released keys
+        unused_keys = [k for k, v in self.keystates.items() if not v]
+        for key in unused_keys:
+            del self.keystates[key]
+
+        # self.keystates contains now the list of currently pressed keys,
+        # release them...
+        for key in self.keystates.keys():
+            self.keystates[key] = False
+
+        # ...and press those that are in parameter
+        for key in keys:
+            self.keystates[key] = True
+
+    def _create_report_data(self):
+        keyboard = KeyboardData()
+        for key, value in self.keystates.items():
+            key = key.replace(" ", "").lower()
+            setattr(keyboard, key, value)
+        return keyboard
+
+    def create_array_report(self, keys, reportID=None, application=None):
+        """
+        Return an input report for this device.
+
+        :param keys: a tuple of chars for the pressed keys. The class maintains
+            the list of currently pressed keys, so to release a key, the caller
+            needs to call again this function without the key in this tuple.
+        :param reportID: the numeric report ID for this report, if needed
+        """
+        self._update_key_state(keys)
+        reportID = reportID or self.default_reportID
+
+        keyboard = self._create_report_data()
+        return self.create_report(keyboard, reportID=reportID, application=application)
+
+    def event(self, keys, reportID=None, application=None):
+        """
+        Send an input event on the default report ID.
+
+        :param keys: a tuple of chars for the pressed keys. The class maintains
+            the list of currently pressed keys, so to release a key, the caller
+            needs to call again this function without the key in this tuple.
+        """
+        r = self.create_array_report(keys, reportID, application)
+        self.call_input_event(r)
+        return [r]
+
+
+class PlainKeyboard(BaseKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,                    # Usage Page (Generic Desktop)
+        0x09, 0x06,                    # Usage (Keyboard)
+        0xa1, 0x01,                    # Collection (Application)
+        0x85, 0x01,                    # .Report ID (1)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0xe0,                    # .Usage Minimum (224)
+        0x29, 0xe7,                    # .Usage Maximum (231)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x25, 0x01,                    # .Logical Maximum (1)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x08,                    # .Report Count (8)
+        0x81, 0x02,                    # .Input (Data,Var,Abs)
+        0x19, 0x00,                    # .Usage Minimum (0)
+        0x29, 0x97,                    # .Usage Maximum (151)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x25, 0x01,                    # .Logical Maximum (1)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x98,                    # .Report Count (152)
+        0x81, 0x02,                    # .Input (Data,Var,Abs)
+        0xc0,                          # End Collection
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+        self.default_reportID = 1
+
+
+class ArrayKeyboard(BaseKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,                    # Usage Page (Generic Desktop)
+        0x09, 0x06,                    # Usage (Keyboard)
+        0xa1, 0x01,                    # Collection (Application)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0xe0,                    # .Usage Minimum (224)
+        0x29, 0xe7,                    # .Usage Maximum (231)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x25, 0x01,                    # .Logical Maximum (1)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x08,                    # .Report Count (8)
+        0x81, 0x02,                    # .Input (Data,Var,Abs)
+        0x95, 0x06,                    # .Report Count (6)
+        0x75, 0x08,                    # .Report Size (8)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x26, 0xa4, 0x00,              # .Logical Maximum (164)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0x00,                    # .Usage Minimum (0)
+        0x29, 0xa4,                    # .Usage Maximum (164)
+        0x81, 0x00,                    # .Input (Data,Arr,Abs)
+        0xc0,                          # End Collection
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+
+    def _create_report_data(self):
+        data = KeyboardData()
+        array = []
+
+        hut = hidtools.hut.HUT
+
+        # strip modifiers from the array
+        for k, v in self.keystates.items():
+            # we ignore depressed keys
+            if not v:
+                continue
+
+            usage = hut[0x07].from_name[k].usage
+            if usage >= 224 and usage <= 231:
+                # modifier
+                setattr(data, k.lower(), 1)
+            else:
+                array.append(k)
+
+        # if array length is bigger than 6, report ErrorRollOver
+        if len(array) > 6:
+            array = ["ErrorRollOver"] * 6
+
+        data.keyboard = array
+        return data
+
+
+class LEDKeyboard(ArrayKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,                    # Usage Page (Generic Desktop)
+        0x09, 0x06,                    # Usage (Keyboard)
+        0xa1, 0x01,                    # Collection (Application)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0xe0,                    # .Usage Minimum (224)
+        0x29, 0xe7,                    # .Usage Maximum (231)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x25, 0x01,                    # .Logical Maximum (1)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x08,                    # .Report Count (8)
+        0x81, 0x02,                    # .Input (Data,Var,Abs)
+        0x95, 0x01,                    # .Report Count (1)
+        0x75, 0x08,                    # .Report Size (8)
+        0x81, 0x01,                    # .Input (Cnst,Arr,Abs)
+        0x95, 0x05,                    # .Report Count (5)
+        0x75, 0x01,                    # .Report Size (1)
+        0x05, 0x08,                    # .Usage Page (LEDs)
+        0x19, 0x01,                    # .Usage Minimum (1)
+        0x29, 0x05,                    # .Usage Maximum (5)
+        0x91, 0x02,                    # .Output (Data,Var,Abs)
+        0x95, 0x01,                    # .Report Count (1)
+        0x75, 0x03,                    # .Report Size (3)
+        0x91, 0x01,                    # .Output (Cnst,Arr,Abs)
+        0x95, 0x06,                    # .Report Count (6)
+        0x75, 0x08,                    # .Report Size (8)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x26, 0xa4, 0x00,              # .Logical Maximum (164)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0x00,                    # .Usage Minimum (0)
+        0x29, 0xa4,                    # .Usage Maximum (164)
+        0x81, 0x00,                    # .Input (Data,Arr,Abs)
+        0xc0,                          # End Collection
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+
+
+# Some Primax manufactured keyboards set the Usage Page after having defined
+# some local Usages. It relies on the fact that the specification states that
+# Usages are to be concatenated with Usage Pages upon finding a Main item (see
+# 6.2.2.8). This test covers this case.
+class PrimaxKeyboard(ArrayKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,                    # Usage Page (Generic Desktop)
+        0x09, 0x06,                    # Usage (Keyboard)
+        0xA1, 0x01,                    # Collection (Application)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x19, 0xE0,                    # .Usage Minimum (224)
+        0x29, 0xE7,                    # .Usage Maximum (231)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x25, 0x01,                    # .Logical Maximum (1)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x08,                    # .Report Count (8)
+        0x81, 0x02,                    # .Input (Data,Var,Abs)
+        0x75, 0x08,                    # .Report Size (8)
+        0x95, 0x01,                    # .Report Count (1)
+        0x81, 0x01,                    # .Input (Data,Var,Abs)
+        0x05, 0x08,                    # .Usage Page (LEDs)
+        0x19, 0x01,                    # .Usage Minimum (1)
+        0x29, 0x03,                    # .Usage Maximum (3)
+        0x75, 0x01,                    # .Report Size (1)
+        0x95, 0x03,                    # .Report Count (3)
+        0x91, 0x02,                    # .Output (Data,Var,Abs)
+        0x95, 0x01,                    # .Report Count (1)
+        0x75, 0x05,                    # .Report Size (5)
+        0x91, 0x01,                    # .Output (Constant)
+        0x15, 0x00,                    # .Logical Minimum (0)
+        0x26, 0xFF, 0x00,              # .Logical Maximum (255)
+        0x19, 0x00,                    # .Usage Minimum (0)
+        0x2A, 0xFF, 0x00,              # .Usage Maximum (255)
+        0x05, 0x07,                    # .Usage Page (Keyboard)
+        0x75, 0x08,                    # .Report Size (8)
+        0x95, 0x06,                    # .Report Count (6)
+        0x81, 0x00,                    # .Input (Data,Arr,Abs)
+        0xC0,                          # End Collection
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+
+
+class BaseTest:
+    class TestKeyboard(base.BaseTestCase.TestUhid):
+        def test_single_key(self):
+            """check for key reliability."""
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev()
+            syn_event = self.syn_event
+
+            r = uhdev.event(["a and A"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
+
+            r = uhdev.event([])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
+
+        def test_two_keys(self):
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev()
+            syn_event = self.syn_event
+
+            r = uhdev.event(["a and A", "q and Q"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
+
+            r = uhdev.event([])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
+            assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0
+
+            r = uhdev.event(["c and C"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
+
+            r = uhdev.event(["c and C", "Spacebar"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
+            assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
+
+            r = uhdev.event(["Spacebar"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_C] == 0
+            assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
+
+            r = uhdev.event([])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+            assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0
+
+        def test_modifiers(self):
+            # ctrl-alt-del would be very nice :)
+            uhdev = self.uhdev
+            syn_event = self.syn_event
+
+            r = uhdev.event(["LeftControl", "LeftShift", "= and +"])
+            expected = [syn_event]
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1))
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1))
+            expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(expected, events)
+
+
+class TestPlainKeyboard(BaseTest.TestKeyboard):
+    def create_device(self):
+        return PlainKeyboard()
+
+    def test_10_keys(self):
+        uhdev = self.uhdev
+        syn_event = self.syn_event
+
+        r = uhdev.event(
+            [
+                "1 and !",
+                "2 and @",
+                "3 and #",
+                "4 and $",
+                "5 and %",
+                "6 and ^",
+                "7 and &",
+                "8 and *",
+                "9 and (",
+                "0 and )",
+            ]
+        )
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+
+class TestArrayKeyboard(BaseTest.TestKeyboard):
+    def create_device(self):
+        return ArrayKeyboard()
+
+    def test_10_keys(self):
+        uhdev = self.uhdev
+        syn_event = self.syn_event
+
+        r = uhdev.event(
+            [
+                "1 and !",
+                "2 and @",
+                "3 and #",
+                "4 and $",
+                "5 and %",
+                "6 and ^",
+            ]
+        )
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
+        events = uhdev.next_sync_events()
+
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+        # ErrRollOver
+        r = uhdev.event(
+            [
+                "1 and !",
+                "2 and @",
+                "3 and #",
+                "4 and $",
+                "5 and %",
+                "6 and ^",
+                "7 and &",
+                "8 and *",
+                "9 and (",
+                "0 and )",
+            ]
+        )
+        events = uhdev.next_sync_events()
+
+        self.debug_reports(r, uhdev, events)
+
+        assert len(events) == 0
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+
+class TestLEDKeyboard(BaseTest.TestKeyboard):
+    def create_device(self):
+        return LEDKeyboard()
+
+
+class TestPrimaxKeyboard(BaseTest.TestKeyboard):
+    def create_device(self):
+        return PrimaxKeyboard()

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 05/11] selftests: hid: import hid-tools hid-mouse tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (3 preceding siblings ...)
  2023-02-17 16:17 ` [PATCH 04/11] selftests: hid: import hid-tools hid-keyboards tests Benjamin Tissoires
@ 2023-02-17 16:17 ` Benjamin Tissoires
  2023-02-17 16:18 ` [PATCH 08/11] selftests: hid: import hid-tools hid-apple tests Benjamin Tissoires
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:17 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Peter Hutterer, Roderick Colenbrander

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: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile            |   1 +
 tools/testing/selftests/hid/hid-mouse.sh        |   7 +
 tools/testing/selftests/hid/tests/test_mouse.py | 977 ++++++++++++++++++++++++
 3 files changed, 985 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 181a594ffe92..00b2cc25e24c 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include
 TEST_PROGS := hid-core.sh
 TEST_PROGS += hid-gamepad.sh
 TEST_PROGS += hid-keyboard.sh
+TEST_PROGS += hid-mouse.sh
 
 CXX ?= $(CROSS_COMPILE)g++
 
diff --git a/tools/testing/selftests/hid/hid-mouse.sh b/tools/testing/selftests/hid/hid-mouse.sh
new file mode 100755
index 000000000000..7b4ad4f646f7
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-mouse.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_mouse.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_mouse.py b/tools/testing/selftests/hid/tests/test_mouse.py
new file mode 100644
index 000000000000..fd2ba62e783a
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_mouse.py
@@ -0,0 +1,977 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2017 Red Hat, Inc.
+#
+
+from . import base
+import hidtools.hid
+from hidtools.util import BusType
+import libevdev
+import logging
+import pytest
+
+logger = logging.getLogger("hidtools.test.mouse")
+
+# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6
+try:
+    libevdev.EV_REL.REL_WHEEL_HI_RES
+except AttributeError:
+    libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B
+    libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C
+
+
+class InvalidHIDCommunication(Exception):
+    pass
+
+
+class MouseData(object):
+    pass
+
+
+class BaseMouse(base.UHIDTestDevice):
+    def __init__(self, rdesc, name=None, input_info=None):
+        assert rdesc is not None
+        super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)
+        self.left = False
+        self.right = False
+        self.middle = False
+
+    def create_report(self, x, y, buttons=None, wheels=None, reportID=None):
+        """
+        Return an input report for this device.
+
+        :param x: relative x
+        :param y: relative y
+        :param buttons: a (l, r, m) tuple of bools for the button states,
+            where ``None`` is "leave unchanged"
+        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
+            the two wheels
+        :param reportID: the numeric report ID for this report, if needed
+        """
+        if buttons is not None:
+            l, r, m = buttons
+            if l is not None:
+                self.left = l
+            if r is not None:
+                self.right = r
+            if m is not None:
+                self.middle = m
+        left = self.left
+        right = self.right
+        middle = self.middle
+        # Note: the BaseMouse doesn't actually have a wheel but the
+        # create_report magic only fills in those fields exist, so let's
+        # make this generic here.
+        wheel, acpan = 0, 0
+        if wheels is not None:
+            if isinstance(wheels, tuple):
+                wheel = wheels[0]
+                acpan = wheels[1]
+            else:
+                wheel = wheels
+
+        reportID = reportID or self.default_reportID
+
+        mouse = MouseData()
+        mouse.b1 = int(left)
+        mouse.b2 = int(right)
+        mouse.b3 = int(middle)
+        mouse.x = x
+        mouse.y = y
+        mouse.wheel = wheel
+        mouse.acpan = acpan
+        return super().create_report(mouse, reportID=reportID)
+
+    def event(self, x, y, buttons=None, wheels=None):
+        """
+        Send an input event on the default report ID.
+
+        :param x: relative x
+        :param y: relative y
+        :param buttons: a (l, r, m) tuple of bools for the button states,
+            where ``None`` is "leave unchanged"
+        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
+            the two wheels
+        """
+        r = self.create_report(x, y, buttons, wheels)
+        self.call_input_event(r)
+        return [r]
+
+
+class ButtonMouse(BaseMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,  # .Usage Page (Generic Desktop)        0
+        0x09, 0x02,  # .Usage (Mouse)                       2
+        0xa1, 0x01,  # .Collection (Application)            4
+        0x09, 0x02,  # ..Usage (Mouse)                      6
+        0xa1, 0x02,  # ..Collection (Logical)               8
+        0x09, 0x01,  # ...Usage (Pointer)                   10
+        0xa1, 0x00,  # ...Collection (Physical)             12
+        0x05, 0x09,  # ....Usage Page (Button)              14
+        0x19, 0x01,  # ....Usage Minimum (1)                16
+        0x29, 0x03,  # ....Usage Maximum (3)                18
+        0x15, 0x00,  # ....Logical Minimum (0)              20
+        0x25, 0x01,  # ....Logical Maximum (1)              22
+        0x75, 0x01,  # ....Report Size (1)                  24
+        0x95, 0x03,  # ....Report Count (3)                 26
+        0x81, 0x02,  # ....Input (Data,Var,Abs)             28
+        0x75, 0x05,  # ....Report Size (5)                  30
+        0x95, 0x01,  # ....Report Count (1)                 32
+        0x81, 0x03,  # ....Input (Cnst,Var,Abs)             34
+        0x05, 0x01,  # ....Usage Page (Generic Desktop)     36
+        0x09, 0x30,  # ....Usage (X)                        38
+        0x09, 0x31,  # ....Usage (Y)                        40
+        0x15, 0x81,  # ....Logical Minimum (-127)           42
+        0x25, 0x7f,  # ....Logical Maximum (127)            44
+        0x75, 0x08,  # ....Report Size (8)                  46
+        0x95, 0x02,  # ....Report Count (2)                 48
+        0x81, 0x06,  # ....Input (Data,Var,Rel)             50
+        0xc0,        # ...End Collection                    52
+        0xc0,        # ..End Collection                     53
+        0xc0,        # .End Collection                      54
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+
+    def fake_report(self, x, y, buttons):
+        if buttons is not None:
+            left, right, middle = buttons
+            if left is None:
+                left = self.left
+            if right is None:
+                right = self.right
+            if middle is None:
+                middle = self.middle
+        else:
+            left = self.left
+            right = self.right
+            middle = self.middle
+
+        button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)
+        x = max(-127, min(127, x))
+        y = max(-127, min(127, y))
+        x = hidtools.util.to_twos_comp(x, 8)
+        y = hidtools.util.to_twos_comp(y, 8)
+        return [button_mask, x, y]
+
+
+class WheelMouse(ButtonMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,  # Usage Page (Generic Desktop)        0
+        0x09, 0x02,  # Usage (Mouse)                       2
+        0xa1, 0x01,  # Collection (Application)            4
+        0x05, 0x09,  # .Usage Page (Button)                6
+        0x19, 0x01,  # .Usage Minimum (1)                  8
+        0x29, 0x03,  # .Usage Maximum (3)                  10
+        0x15, 0x00,  # .Logical Minimum (0)                12
+        0x25, 0x01,  # .Logical Maximum (1)                14
+        0x95, 0x03,  # .Report Count (3)                   16
+        0x75, 0x01,  # .Report Size (1)                    18
+        0x81, 0x02,  # .Input (Data,Var,Abs)               20
+        0x95, 0x01,  # .Report Count (1)                   22
+        0x75, 0x05,  # .Report Size (5)                    24
+        0x81, 0x03,  # .Input (Cnst,Var,Abs)               26
+        0x05, 0x01,  # .Usage Page (Generic Desktop)       28
+        0x09, 0x01,  # .Usage (Pointer)                    30
+        0xa1, 0x00,  # .Collection (Physical)              32
+        0x09, 0x30,  # ..Usage (X)                         34
+        0x09, 0x31,  # ..Usage (Y)                         36
+        0x15, 0x81,  # ..Logical Minimum (-127)            38
+        0x25, 0x7f,  # ..Logical Maximum (127)             40
+        0x75, 0x08,  # ..Report Size (8)                   42
+        0x95, 0x02,  # ..Report Count (2)                  44
+        0x81, 0x06,  # ..Input (Data,Var,Rel)              46
+        0xc0,        # .End Collection                     48
+        0x09, 0x38,  # .Usage (Wheel)                      49
+        0x15, 0x81,  # .Logical Minimum (-127)             51
+        0x25, 0x7f,  # .Logical Maximum (127)              53
+        0x75, 0x08,  # .Report Size (8)                    55
+        0x95, 0x01,  # .Report Count (1)                   57
+        0x81, 0x06,  # .Input (Data,Var,Rel)               59
+        0xc0,        # End Collection                      61
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+        self.wheel_multiplier = 1
+
+
+class TwoWheelMouse(WheelMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,        # Usage Page (Generic Desktop)        0
+        0x09, 0x02,        # Usage (Mouse)                       2
+        0xa1, 0x01,        # Collection (Application)            4
+        0x09, 0x01,        # .Usage (Pointer)                    6
+        0xa1, 0x00,        # .Collection (Physical)              8
+        0x05, 0x09,        # ..Usage Page (Button)               10
+        0x19, 0x01,        # ..Usage Minimum (1)                 12
+        0x29, 0x10,        # ..Usage Maximum (16)                14
+        0x15, 0x00,        # ..Logical Minimum (0)               16
+        0x25, 0x01,        # ..Logical Maximum (1)               18
+        0x95, 0x10,        # ..Report Count (16)                 20
+        0x75, 0x01,        # ..Report Size (1)                   22
+        0x81, 0x02,        # ..Input (Data,Var,Abs)              24
+        0x05, 0x01,        # ..Usage Page (Generic Desktop)      26
+        0x16, 0x01, 0x80,  # ..Logical Minimum (-32767)          28
+        0x26, 0xff, 0x7f,  # ..Logical Maximum (32767)           31
+        0x75, 0x10,        # ..Report Size (16)                  34
+        0x95, 0x02,        # ..Report Count (2)                  36
+        0x09, 0x30,        # ..Usage (X)                         38
+        0x09, 0x31,        # ..Usage (Y)                         40
+        0x81, 0x06,        # ..Input (Data,Var,Rel)              42
+        0x15, 0x81,        # ..Logical Minimum (-127)            44
+        0x25, 0x7f,        # ..Logical Maximum (127)             46
+        0x75, 0x08,        # ..Report Size (8)                   48
+        0x95, 0x01,        # ..Report Count (1)                  50
+        0x09, 0x38,        # ..Usage (Wheel)                     52
+        0x81, 0x06,        # ..Input (Data,Var,Rel)              54
+        0x05, 0x0c,        # ..Usage Page (Consumer Devices)     56
+        0x0a, 0x38, 0x02,  # ..Usage (AC Pan)                    58
+        0x95, 0x01,        # ..Report Count (1)                  61
+        0x81, 0x06,        # ..Input (Data,Var,Rel)              63
+        0xc0,              # .End Collection                     65
+        0xc0,              # End Collection                      66
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+        self.hwheel_multiplier = 1
+
+
+class MIDongleMIWirelessMouse(TwoWheelMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,         # Usage Page (Generic Desktop)
+        0x09, 0x02,         # Usage (Mouse)
+        0xa1, 0x01,         # Collection (Application)
+        0x85, 0x01,         # .Report ID (1)
+        0x09, 0x01,         # .Usage (Pointer)
+        0xa1, 0x00,         # .Collection (Physical)
+        0x95, 0x05,         # ..Report Count (5)
+        0x75, 0x01,         # ..Report Size (1)
+        0x05, 0x09,         # ..Usage Page (Button)
+        0x19, 0x01,         # ..Usage Minimum (1)
+        0x29, 0x05,         # ..Usage Maximum (5)
+        0x15, 0x00,         # ..Logical Minimum (0)
+        0x25, 0x01,         # ..Logical Maximum (1)
+        0x81, 0x02,         # ..Input (Data,Var,Abs)
+        0x95, 0x01,         # ..Report Count (1)
+        0x75, 0x03,         # ..Report Size (3)
+        0x81, 0x01,         # ..Input (Cnst,Arr,Abs)
+        0x75, 0x08,         # ..Report Size (8)
+        0x95, 0x01,         # ..Report Count (1)
+        0x05, 0x01,         # ..Usage Page (Generic Desktop)
+        0x09, 0x38,         # ..Usage (Wheel)
+        0x15, 0x81,         # ..Logical Minimum (-127)
+        0x25, 0x7f,         # ..Logical Maximum (127)
+        0x81, 0x06,         # ..Input (Data,Var,Rel)
+        0x05, 0x0c,         # ..Usage Page (Consumer Devices)
+        0x0a, 0x38, 0x02,   # ..Usage (AC Pan)
+        0x95, 0x01,         # ..Report Count (1)
+        0x81, 0x06,         # ..Input (Data,Var,Rel)
+        0xc0,               # .End Collection
+        0x85, 0x02,         # .Report ID (2)
+        0x09, 0x01,         # .Usage (Consumer Control)
+        0xa1, 0x00,         # .Collection (Physical)
+        0x75, 0x0c,         # ..Report Size (12)
+        0x95, 0x02,         # ..Report Count (2)
+        0x05, 0x01,         # ..Usage Page (Generic Desktop)
+        0x09, 0x30,         # ..Usage (X)
+        0x09, 0x31,         # ..Usage (Y)
+        0x16, 0x01, 0xf8,   # ..Logical Minimum (-2047)
+        0x26, 0xff, 0x07,   # ..Logical Maximum (2047)
+        0x81, 0x06,         # ..Input (Data,Var,Rel)
+        0xc0,               # .End Collection
+        0xc0,               # End Collection
+        0x05, 0x0c,         # Usage Page (Consumer Devices)
+        0x09, 0x01,         # Usage (Consumer Control)
+        0xa1, 0x01,         # Collection (Application)
+        0x85, 0x03,         # .Report ID (3)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x25, 0x01,         # .Logical Maximum (1)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x01,         # .Report Count (1)
+        0x09, 0xcd,         # .Usage (Play/Pause)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x0a, 0x83, 0x01,   # .Usage (AL Consumer Control Config)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x09, 0xb5,         # .Usage (Scan Next Track)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x09, 0xb6,         # .Usage (Scan Previous Track)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x09, 0xea,         # .Usage (Volume Down)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x09, 0xe9,         # .Usage (Volume Up)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x0a, 0x25, 0x02,   # .Usage (AC Forward)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0x0a, 0x24, 0x02,   # .Usage (AC Back)
+        0x81, 0x06,         # .Input (Data,Var,Rel)
+        0xc0,               # End Collection
+    ]
+    # fmt: on
+    device_input_info = (BusType.USB, 0x2717, 0x003B)
+    device_name = "uhid test MI Dongle MI Wireless Mouse"
+
+    def __init__(
+        self, rdesc=report_descriptor, name=device_name, input_info=device_input_info
+    ):
+        super().__init__(rdesc, name, input_info)
+
+    def event(self, x, y, buttons=None, wheels=None):
+        # this mouse spreads the relative pointer and the mouse buttons
+        # onto 2 distinct reports
+        rs = []
+        r = self.create_report(x, y, buttons, wheels, reportID=1)
+        self.call_input_event(r)
+        rs.append(r)
+        r = self.create_report(x, y, buttons, reportID=2)
+        self.call_input_event(r)
+        rs.append(r)
+        return rs
+
+
+class ResolutionMultiplierMouse(TwoWheelMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,        # Usage Page (Generic Desktop)        83
+        0x09, 0x02,        # Usage (Mouse)                       85
+        0xa1, 0x01,        # Collection (Application)            87
+        0x05, 0x01,        # .Usage Page (Generic Desktop)       89
+        0x09, 0x02,        # .Usage (Mouse)                      91
+        0xa1, 0x02,        # .Collection (Logical)               93
+        0x85, 0x11,        # ..Report ID (17)                    95
+        0x09, 0x01,        # ..Usage (Pointer)                   97
+        0xa1, 0x00,        # ..Collection (Physical)             99
+        0x05, 0x09,        # ...Usage Page (Button)              101
+        0x19, 0x01,        # ...Usage Minimum (1)                103
+        0x29, 0x03,        # ...Usage Maximum (3)                105
+        0x95, 0x03,        # ...Report Count (3)                 107
+        0x75, 0x01,        # ...Report Size (1)                  109
+        0x25, 0x01,        # ...Logical Maximum (1)              111
+        0x81, 0x02,        # ...Input (Data,Var,Abs)             113
+        0x95, 0x01,        # ...Report Count (1)                 115
+        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             117
+        0x09, 0x05,        # ...Usage (Vendor Usage 0x05)        119
+        0x81, 0x02,        # ...Input (Data,Var,Abs)             121
+        0x95, 0x03,        # ...Report Count (3)                 123
+        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             125
+        0x05, 0x01,        # ...Usage Page (Generic Desktop)     127
+        0x09, 0x30,        # ...Usage (X)                        129
+        0x09, 0x31,        # ...Usage (Y)                        131
+        0x95, 0x02,        # ...Report Count (2)                 133
+        0x75, 0x08,        # ...Report Size (8)                  135
+        0x15, 0x81,        # ...Logical Minimum (-127)           137
+        0x25, 0x7f,        # ...Logical Maximum (127)            139
+        0x81, 0x06,        # ...Input (Data,Var,Rel)             141
+        0xa1, 0x02,        # ...Collection (Logical)             143
+        0x85, 0x12,        # ....Report ID (18)                  145
+        0x09, 0x48,        # ....Usage (Resolution Multiplier)   147
+        0x95, 0x01,        # ....Report Count (1)                149
+        0x75, 0x02,        # ....Report Size (2)                 151
+        0x15, 0x00,        # ....Logical Minimum (0)             153
+        0x25, 0x01,        # ....Logical Maximum (1)             155
+        0x35, 0x01,        # ....Physical Minimum (1)            157
+        0x45, 0x04,        # ....Physical Maximum (4)            159
+        0xb1, 0x02,        # ....Feature (Data,Var,Abs)          161
+        0x35, 0x00,        # ....Physical Minimum (0)            163
+        0x45, 0x00,        # ....Physical Maximum (0)            165
+        0x75, 0x06,        # ....Report Size (6)                 167
+        0xb1, 0x01,        # ....Feature (Cnst,Arr,Abs)          169
+        0x85, 0x11,        # ....Report ID (17)                  171
+        0x09, 0x38,        # ....Usage (Wheel)                   173
+        0x15, 0x81,        # ....Logical Minimum (-127)          175
+        0x25, 0x7f,        # ....Logical Maximum (127)           177
+        0x75, 0x08,        # ....Report Size (8)                 179
+        0x81, 0x06,        # ....Input (Data,Var,Rel)            181
+        0xc0,              # ...End Collection                   183
+        0x05, 0x0c,        # ...Usage Page (Consumer Devices)    184
+        0x75, 0x08,        # ...Report Size (8)                  186
+        0x0a, 0x38, 0x02,  # ...Usage (AC Pan)                   188
+        0x81, 0x06,        # ...Input (Data,Var,Rel)             191
+        0xc0,              # ..End Collection                    193
+        0xc0,              # .End Collection                     194
+        0xc0,              # End Collection                      195
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+        self.default_reportID = 0x11
+
+        # Feature Report 12, multiplier Feature value must be set to 0b01,
+        # i.e. 1. We should extract that from the descriptor instead
+        # of hardcoding it here, but meanwhile this will do.
+        self.set_feature_report = [0x12, 0x1]
+
+    def set_report(self, req, rnum, rtype, data):
+        if rtype != self.UHID_FEATURE_REPORT:
+            raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
+        if rnum != 0x12:
+            raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
+
+        if data != self.set_feature_report:
+            raise InvalidHIDCommunication(
+                f"Unexpected data: {data}, expected {self.set_feature_report}"
+            )
+
+        self.wheel_multiplier = 4
+
+        return 0
+
+
+class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
+    def set_report(self, req, rnum, rtype, data):
+        super().set_report(req, rnum, rtype, data)
+
+        self.wheel_multiplier = 1
+        self.hwheel_multiplier = 1
+        return 32  # EPIPE
+
+
+class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,         # Usage Page (Generic Desktop)        0
+        0x09, 0x02,         # Usage (Mouse)                       2
+        0xa1, 0x01,         # Collection (Application)            4
+        0x05, 0x01,         # .Usage Page (Generic Desktop)       6
+        0x09, 0x02,         # .Usage (Mouse)                      8
+        0xa1, 0x02,         # .Collection (Logical)               10
+        0x85, 0x1a,         # ..Report ID (26)                    12
+        0x09, 0x01,         # ..Usage (Pointer)                   14
+        0xa1, 0x00,         # ..Collection (Physical)             16
+        0x05, 0x09,         # ...Usage Page (Button)              18
+        0x19, 0x01,         # ...Usage Minimum (1)                20
+        0x29, 0x05,         # ...Usage Maximum (5)                22
+        0x95, 0x05,         # ...Report Count (5)                 24
+        0x75, 0x01,         # ...Report Size (1)                  26
+        0x15, 0x00,         # ...Logical Minimum (0)              28
+        0x25, 0x01,         # ...Logical Maximum (1)              30
+        0x81, 0x02,         # ...Input (Data,Var,Abs)             32
+        0x75, 0x03,         # ...Report Size (3)                  34
+        0x95, 0x01,         # ...Report Count (1)                 36
+        0x81, 0x01,         # ...Input (Cnst,Arr,Abs)             38
+        0x05, 0x01,         # ...Usage Page (Generic Desktop)     40
+        0x09, 0x30,         # ...Usage (X)                        42
+        0x09, 0x31,         # ...Usage (Y)                        44
+        0x95, 0x02,         # ...Report Count (2)                 46
+        0x75, 0x10,         # ...Report Size (16)                 48
+        0x16, 0x01, 0x80,   # ...Logical Minimum (-32767)         50
+        0x26, 0xff, 0x7f,   # ...Logical Maximum (32767)          53
+        0x81, 0x06,         # ...Input (Data,Var,Rel)             56
+        0xa1, 0x02,         # ...Collection (Logical)             58
+        0x85, 0x12,         # ....Report ID (18)                  60
+        0x09, 0x48,         # ....Usage (Resolution Multiplier)   62
+        0x95, 0x01,         # ....Report Count (1)                64
+        0x75, 0x02,         # ....Report Size (2)                 66
+        0x15, 0x00,         # ....Logical Minimum (0)             68
+        0x25, 0x01,         # ....Logical Maximum (1)             70
+        0x35, 0x01,         # ....Physical Minimum (1)            72
+        0x45, 0x0c,         # ....Physical Maximum (12)           74
+        0xb1, 0x02,         # ....Feature (Data,Var,Abs)          76
+        0x85, 0x1a,         # ....Report ID (26)                  78
+        0x09, 0x38,         # ....Usage (Wheel)                   80
+        0x35, 0x00,         # ....Physical Minimum (0)            82
+        0x45, 0x00,         # ....Physical Maximum (0)            84
+        0x95, 0x01,         # ....Report Count (1)                86
+        0x75, 0x10,         # ....Report Size (16)                88
+        0x16, 0x01, 0x80,   # ....Logical Minimum (-32767)        90
+        0x26, 0xff, 0x7f,   # ....Logical Maximum (32767)         93
+        0x81, 0x06,         # ....Input (Data,Var,Rel)            96
+        0xc0,               # ...End Collection                   98
+        0xa1, 0x02,         # ...Collection (Logical)             99
+        0x85, 0x12,         # ....Report ID (18)                  101
+        0x09, 0x48,         # ....Usage (Resolution Multiplier)   103
+        0x75, 0x02,         # ....Report Size (2)                 105
+        0x15, 0x00,         # ....Logical Minimum (0)             107
+        0x25, 0x01,         # ....Logical Maximum (1)             109
+        0x35, 0x01,         # ....Physical Minimum (1)            111
+        0x45, 0x0c,         # ....Physical Maximum (12)           113
+        0xb1, 0x02,         # ....Feature (Data,Var,Abs)          115
+        0x35, 0x00,         # ....Physical Minimum (0)            117
+        0x45, 0x00,         # ....Physical Maximum (0)            119
+        0x75, 0x04,         # ....Report Size (4)                 121
+        0xb1, 0x01,         # ....Feature (Cnst,Arr,Abs)          123
+        0x85, 0x1a,         # ....Report ID (26)                  125
+        0x05, 0x0c,         # ....Usage Page (Consumer Devices)   127
+        0x95, 0x01,         # ....Report Count (1)                129
+        0x75, 0x10,         # ....Report Size (16)                131
+        0x16, 0x01, 0x80,   # ....Logical Minimum (-32767)        133
+        0x26, 0xff, 0x7f,   # ....Logical Maximum (32767)         136
+        0x0a, 0x38, 0x02,   # ....Usage (AC Pan)                  139
+        0x81, 0x06,         # ....Input (Data,Var,Rel)            142
+        0xc0,               # ...End Collection                   144
+        0xc0,               # ..End Collection                    145
+        0xc0,               # .End Collection                     146
+        0xc0,               # End Collection                      147
+    ]
+    # fmt: on
+
+    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
+        super().__init__(rdesc, name, input_info)
+        self.default_reportID = 0x1A
+
+        # Feature Report 12, multiplier Feature value must be set to 0b0101,
+        # i.e. 5. We should extract that from the descriptor instead
+        # of hardcoding it here, but meanwhile this will do.
+        self.set_feature_report = [0x12, 0x5]
+
+    def set_report(self, req, rnum, rtype, data):
+        super().set_report(req, rnum, rtype, data)
+
+        self.wheel_multiplier = 12
+        self.hwheel_multiplier = 12
+
+        return 0
+
+
+class BaseTest:
+    class TestMouse(base.BaseTestCase.TestUhid):
+        def test_buttons(self):
+            """check for button reliability."""
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev()
+            syn_event = self.syn_event
+
+            r = uhdev.event(0, 0, (None, True, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
+
+            r = uhdev.event(0, 0, (None, False, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
+
+            r = uhdev.event(0, 0, (None, None, True))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1
+
+            r = uhdev.event(0, 0, (None, None, False))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0
+
+            r = uhdev.event(0, 0, (True, None, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
+
+            r = uhdev.event(0, 0, (False, None, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
+
+            r = uhdev.event(0, 0, (True, True, None))
+            expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
+            expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn(
+                (syn_event, expected_event0, expected_event1), events
+            )
+            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
+            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
+
+            r = uhdev.event(0, 0, (False, None, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
+            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
+
+            r = uhdev.event(0, 0, (None, False, None))
+            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEventsIn((syn_event, expected_event), events)
+            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
+            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
+
+        def test_relative(self):
+            """Check for relative events."""
+            uhdev = self.uhdev
+
+            syn_event = self.syn_event
+
+            r = uhdev.event(0, -1)
+            expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEvents((syn_event, expected_event), events)
+
+            r = uhdev.event(1, 0)
+            expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEvents((syn_event, expected_event), events)
+
+            r = uhdev.event(-1, 2)
+            expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)
+            expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEvents(
+                (syn_event, expected_event0, expected_event1), events
+            )
+
+
+class TestSimpleMouse(BaseTest.TestMouse):
+    def create_device(self):
+        return ButtonMouse()
+
+    def test_rdesc(self):
+        """Check that the testsuite actually manages to format the
+        reports according to the report descriptors.
+        No kernel device is used here"""
+        uhdev = self.uhdev
+
+        event = (0, 0, (None, None, None))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (0, 0, (None, True, None))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (0, 0, (True, True, None))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (0, 0, (False, False, False))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (1, 0, (True, False, True))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (-1, 0, (True, False, True))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (-5, 5, (True, False, True))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (-127, 127, (True, False, True))
+        assert uhdev.fake_report(*event) == uhdev.create_report(*event)
+
+        event = (0, -128, (True, False, True))
+        with pytest.raises(hidtools.hid.RangeError):
+            uhdev.create_report(*event)
+
+
+class TestWheelMouse(BaseTest.TestMouse):
+    def create_device(self):
+        return WheelMouse()
+
+    def is_wheel_highres(self, uhdev):
+        evdev = uhdev.get_evdev()
+        assert evdev.has(libevdev.EV_REL.REL_WHEEL)
+        return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)
+
+    def test_wheel(self):
+        uhdev = self.uhdev
+
+        # check if the kernel is high res wheel compatible
+        high_res_wheel = self.is_wheel_highres(uhdev)
+
+        syn_event = self.syn_event
+        # The Resolution Multiplier is applied to the HID reports, so we
+        # need to pre-multiply too.
+        mult = uhdev.wheel_multiplier
+
+        r = uhdev.event(0, 0, wheels=1 * mult)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
+        if high_res_wheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(0, 0, wheels=-1 * mult)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))
+        if high_res_wheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(-1, 2, wheels=3 * mult)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))
+        if high_res_wheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+
+class TestTwoWheelMouse(TestWheelMouse):
+    def create_device(self):
+        return TwoWheelMouse()
+
+    def is_hwheel_highres(self, uhdev):
+        evdev = uhdev.get_evdev()
+        assert evdev.has(libevdev.EV_REL.REL_HWHEEL)
+        return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)
+
+    def test_ac_pan(self):
+        uhdev = self.uhdev
+
+        # check if the kernel is high res wheel compatible
+        high_res_wheel = self.is_wheel_highres(uhdev)
+        high_res_hwheel = self.is_hwheel_highres(uhdev)
+        assert high_res_wheel == high_res_hwheel
+
+        syn_event = self.syn_event
+        # The Resolution Multiplier is applied to the HID reports, so we
+        # need to pre-multiply too.
+        hmult = uhdev.hwheel_multiplier
+        vmult = uhdev.wheel_multiplier
+
+        r = uhdev.event(0, 0, wheels=(0, 1 * hmult))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
+        if high_res_hwheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(0, 0, wheels=(0, -1 * hmult))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))
+        if high_res_hwheel:
+            expected.append(
+                libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)
+            )
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))
+        if high_res_hwheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))
+        if high_res_wheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))
+        if high_res_wheel:
+            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+
+class TestResolutionMultiplierMouse(TestTwoWheelMouse):
+    def create_device(self):
+        return ResolutionMultiplierMouse()
+
+    def is_wheel_highres(self, uhdev):
+        high_res = super().is_wheel_highres(uhdev)
+
+        if not high_res:
+            # the kernel doesn't seem to support the high res wheel mice,
+            # make sure we haven't triggered the feature
+            assert uhdev.wheel_multiplier == 1
+
+        return high_res
+
+    def test_resolution_multiplier_wheel(self):
+        uhdev = self.uhdev
+
+        if not self.is_wheel_highres(uhdev):
+            pytest.skip("Kernel not compatible, we can not trigger the conditions")
+
+        assert uhdev.wheel_multiplier > 1
+        assert 120 % uhdev.wheel_multiplier == 0
+
+    def test_wheel_with_multiplier(self):
+        uhdev = self.uhdev
+
+        if not self.is_wheel_highres(uhdev):
+            pytest.skip("Kernel not compatible, we can not trigger the conditions")
+
+        assert uhdev.wheel_multiplier > 1
+
+        syn_event = self.syn_event
+        mult = uhdev.wheel_multiplier
+
+        r = uhdev.event(0, 0, wheels=1)
+        expected = [syn_event]
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
+        )
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(0, 0, wheels=-1)
+        expected = [syn_event]
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)
+        )
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
+        )
+
+        for _ in range(mult - 1):
+            r = uhdev.event(1, -2, wheels=1)
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEvents(expected, events)
+
+        r = uhdev.event(1, -2, wheels=1)
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+
+class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):
+    def create_device(self):
+        return BadResolutionMultiplierMouse()
+
+    def is_wheel_highres(self, uhdev):
+        high_res = super().is_wheel_highres(uhdev)
+
+        assert uhdev.wheel_multiplier == 1
+
+        return high_res
+
+    def test_resolution_multiplier_wheel(self):
+        uhdev = self.uhdev
+
+        assert uhdev.wheel_multiplier == 1
+
+
+class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):
+    def create_device(self):
+        return ResolutionMultiplierHWheelMouse()
+
+    def is_hwheel_highres(self, uhdev):
+        high_res = super().is_hwheel_highres(uhdev)
+
+        if not high_res:
+            # the kernel doesn't seem to support the high res wheel mice,
+            # make sure we haven't triggered the feature
+            assert uhdev.hwheel_multiplier == 1
+
+        return high_res
+
+    def test_resolution_multiplier_ac_pan(self):
+        uhdev = self.uhdev
+
+        if not self.is_hwheel_highres(uhdev):
+            pytest.skip("Kernel not compatible, we can not trigger the conditions")
+
+        assert uhdev.hwheel_multiplier > 1
+        assert 120 % uhdev.hwheel_multiplier == 0
+
+    def test_ac_pan_with_multiplier(self):
+        uhdev = self.uhdev
+
+        if not self.is_hwheel_highres(uhdev):
+            pytest.skip("Kernel not compatible, we can not trigger the conditions")
+
+        assert uhdev.hwheel_multiplier > 1
+
+        syn_event = self.syn_event
+        hmult = uhdev.hwheel_multiplier
+
+        r = uhdev.event(0, 0, wheels=(0, 1))
+        expected = [syn_event]
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
+        )
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        r = uhdev.event(0, 0, wheels=(0, -1))
+        expected = [syn_event]
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)
+        )
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
+        expected.append(
+            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
+        )
+
+        for _ in range(hmult - 1):
+            r = uhdev.event(1, -2, wheels=(0, 1))
+            events = uhdev.next_sync_events()
+            self.debug_reports(r, uhdev, events)
+            self.assertInputEvents(expected, events)
+
+        r = uhdev.event(1, -2, wheels=(0, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEvents(expected, events)
+
+
+class TestMiMouse(TestWheelMouse):
+    def create_device(self):
+        return MIDongleMIWirelessMouse()
+
+    def assertInputEvents(self, expected_events, effective_events):
+        # Buttons and x/y are spread over two HID reports, so we can get two
+        # event frames for this device.
+        remaining = self.assertInputEventsIn(expected_events, effective_events)
+        try:
+            remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))
+        except ValueError:
+            # If there's no SYN_REPORT in the list, continue and let the
+            # assert below print out the real error
+            pass
+        assert remaining == []

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 08/11] selftests: hid: import hid-tools hid-apple tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (4 preceding siblings ...)
  2023-02-17 16:17 ` [PATCH 05/11] selftests: hid: import hid-tools hid-mouse tests Benjamin Tissoires
@ 2023-02-17 16:18 ` Benjamin Tissoires
  2023-02-17 16:18 ` [PATCH 09/11] selftests: hid: import hid-tools hid-ite tests Benjamin Tissoires
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:18 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Roderick Colenbrander

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: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile               |   1 +
 tools/testing/selftests/hid/config                 |   1 +
 tools/testing/selftests/hid/hid-apple.sh           |   7 +
 .../selftests/hid/tests/test_apple_keyboard.py     | 440 +++++++++++++++++++++
 4 files changed, 449 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 4f11e865bbb3..ce03c65bfba0 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch
 include ../../../scripts/Makefile.include
 
 TEST_PROGS := hid-core.sh
+TEST_PROGS += hid-apple.sh
 TEST_PROGS += hid-gamepad.sh
 TEST_PROGS += hid-keyboard.sh
 TEST_PROGS += hid-mouse.sh
diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config
index 266fbd84ae9c..52b527cc2260 100644
--- a/tools/testing/selftests/hid/config
+++ b/tools/testing/selftests/hid/config
@@ -21,5 +21,6 @@ CONFIG_INPUT_EVDEV=y
 CONFIG_UHID=y
 CONFIG_USB=y
 CONFIG_USB_HID=y
+CONFIG_HID_APPLE=y
 CONFIG_HID_MULTITOUCH=y
 CONFIG_HID_WACOM=y
diff --git a/tools/testing/selftests/hid/hid-apple.sh b/tools/testing/selftests/hid/hid-apple.sh
new file mode 100755
index 000000000000..656f2d5ae5a9
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-apple.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_apple_keyboard.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_apple_keyboard.py b/tools/testing/selftests/hid/tests/test_apple_keyboard.py
new file mode 100644
index 000000000000..f81071d46166
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_apple_keyboard.py
@@ -0,0 +1,440 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2019 Red Hat, Inc.
+#
+
+from .test_keyboard import ArrayKeyboard, TestArrayKeyboard
+from hidtools.util import BusType
+
+import libevdev
+import logging
+
+logger = logging.getLogger("hidtools.test.apple-keyboard")
+
+KERNEL_MODULE = ("apple", "hid-apple")
+
+
+class KbdData(object):
+    pass
+
+
+class AppleKeyboard(ArrayKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,         # Usage Page (Generic Desktop)
+        0x09, 0x06,         # Usage (Keyboard)
+        0xa1, 0x01,         # Collection (Application)
+        0x85, 0x01,         # .Report ID (1)
+        0x05, 0x07,         # .Usage Page (Keyboard)
+        0x19, 0xe0,         # .Usage Minimum (224)
+        0x29, 0xe7,         # .Usage Maximum (231)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x25, 0x01,         # .Logical Maximum (1)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x08,         # .Report Count (8)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x75, 0x08,         # .Report Size (8)
+        0x95, 0x01,         # .Report Count (1)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x05,         # .Report Count (5)
+        0x05, 0x08,         # .Usage Page (LEDs)
+        0x19, 0x01,         # .Usage Minimum (1)
+        0x29, 0x05,         # .Usage Maximum (5)
+        0x91, 0x02,         # .Output (Data,Var,Abs)
+        0x75, 0x03,         # .Report Size (3)
+        0x95, 0x01,         # .Report Count (1)
+        0x91, 0x01,         # .Output (Cnst,Arr,Abs)
+        0x75, 0x08,         # .Report Size (8)
+        0x95, 0x06,         # .Report Count (6)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x26, 0xff, 0x00,   # .Logical Maximum (255)
+        0x05, 0x07,         # .Usage Page (Keyboard)
+        0x19, 0x00,         # .Usage Minimum (0)
+        0x2a, 0xff, 0x00,   # .Usage Maximum (255)
+        0x81, 0x00,         # .Input (Data,Arr,Abs)
+        0xc0,               # End Collection
+        0x05, 0x0c,         # Usage Page (Consumer Devices)
+        0x09, 0x01,         # Usage (Consumer Control)
+        0xa1, 0x01,         # Collection (Application)
+        0x85, 0x47,         # .Report ID (71)
+        0x05, 0x01,         # .Usage Page (Generic Desktop)
+        0x09, 0x06,         # .Usage (Keyboard)
+        0xa1, 0x02,         # .Collection (Logical)
+        0x05, 0x06,         # ..Usage Page (Generic Device Controls)
+        0x09, 0x20,         # ..Usage (Battery Strength)
+        0x15, 0x00,         # ..Logical Minimum (0)
+        0x26, 0xff, 0x00,   # ..Logical Maximum (255)
+        0x75, 0x08,         # ..Report Size (8)
+        0x95, 0x01,         # ..Report Count (1)
+        0x81, 0x02,         # ..Input (Data,Var,Abs)
+        0xc0,               # .End Collection
+        0xc0,               # End Collection
+        0x05, 0x0c,         # Usage Page (Consumer Devices)
+        0x09, 0x01,         # Usage (Consumer Control)
+        0xa1, 0x01,         # Collection (Application)
+        0x85, 0x11,         # .Report ID (17)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x25, 0x01,         # .Logical Maximum (1)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x03,         # .Report Count (3)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x01,         # .Report Count (1)
+        0x05, 0x0c,         # .Usage Page (Consumer Devices)
+        0x09, 0xb8,         # .Usage (Eject)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x06, 0xff, 0x00,   # .Usage Page (Vendor Usage Page 0xff)
+        0x09, 0x03,         # .Usage (Vendor Usage 0x03)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x03,         # .Report Count (3)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x05, 0x0c,         # .Usage Page (Consumer Devices)
+        0x85, 0x12,         # .Report ID (18)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x25, 0x01,         # .Logical Maximum (1)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x01,         # .Report Count (1)
+        0x09, 0xcd,         # .Usage (Play/Pause)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x09, 0xb3,         # .Usage (Fast Forward)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x09, 0xb4,         # .Usage (Rewind)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x09, 0xb5,         # .Usage (Scan Next Track)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x09, 0xb6,         # .Usage (Scan Previous Track)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x85, 0x13,         # .Report ID (19)
+        0x15, 0x00,         # .Logical Minimum (0)
+        0x25, 0x01,         # .Logical Maximum (1)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x01,         # .Report Count (1)
+        0x06, 0x01, 0xff,   # .Usage Page (Vendor Usage Page 0xff01)
+        0x09, 0x0a,         # .Usage (Vendor Usage 0x0a)
+        0x81, 0x02,         # .Input (Data,Var,Abs)
+        0x06, 0x01, 0xff,   # .Usage Page (Vendor Usage Page 0xff01)
+        0x09, 0x0c,         # .Usage (Vendor Usage 0x0c)
+        0x81, 0x22,         # .Input (Data,Var,Abs,NoPref)
+        0x75, 0x01,         # .Report Size (1)
+        0x95, 0x06,         # .Report Count (6)
+        0x81, 0x01,         # .Input (Cnst,Arr,Abs)
+        0x85, 0x09,         # .Report ID (9)
+        0x09, 0x0b,         # .Usage (Vendor Usage 0x0b)
+        0x75, 0x08,         # .Report Size (8)
+        0x95, 0x01,         # .Report Count (1)
+        0xb1, 0x02,         # .Feature (Data,Var,Abs)
+        0x75, 0x08,         # .Report Size (8)
+        0x95, 0x02,         # .Report Count (2)
+        0xb1, 0x01,         # .Feature (Cnst,Arr,Abs)
+        0xc0,               # End Collection
+    ]
+    # fmt: on
+
+    def __init__(
+        self,
+        rdesc=report_descriptor,
+        name="Apple Wireless Keyboard",
+        input_info=(BusType.BLUETOOTH, 0x05AC, 0x0256),
+    ):
+        super().__init__(rdesc, name, input_info)
+        self.default_reportID = 1
+
+    def send_fn_state(self, state):
+        data = KbdData()
+        setattr(data, "0xff0003", state)
+        r = self.create_report(data, reportID=17)
+        self.call_input_event(r)
+        return [r]
+
+
+class TestAppleKeyboard(TestArrayKeyboard):
+    kernel_modules = [KERNEL_MODULE]
+
+    def create_device(self):
+        return AppleKeyboard()
+
+    def test_single_function_key(self):
+        """check for function key reliability."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.event(["F4"])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
+
+    def test_single_fn_function_key(self):
+        """check for function key reliability with the fn key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.send_fn_state(1)
+        r.extend(uhdev.event(["F4"]))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        r = uhdev.send_fn_state(0)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+    def test_single_fn_function_key_release_first(self):
+        """check for function key reliability with the fn key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.send_fn_state(1)
+        r.extend(uhdev.event(["F4"]))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
+
+        r = uhdev.send_fn_state(0)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+
+    def test_single_fn_function_key_inverted(self):
+        """check for function key reliability with the fn key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.event(["F4"])
+        r.extend(uhdev.send_fn_state(1))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        r = uhdev.send_fn_state(0)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+    def test_multiple_fn_function_key_release_first(self):
+        """check for function key reliability with the fn key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.send_fn_state(1)
+        r.extend(uhdev.event(["F4"]))
+        r.extend(uhdev.event(["F4", "F6"]))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        r = uhdev.event(["F6"])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        r = uhdev.send_fn_state(0)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+    def test_multiple_fn_function_key_release_between(self):
+        """check for function key reliability with the fn key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        # press F4
+        r = uhdev.event(["F4"])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+        # press Fn key
+        r = uhdev.send_fn_state(1)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        # keep F4 and press F6
+        r = uhdev.event(["F4", "F6"])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        # keep F4 and F6
+        r = uhdev.event(["F4", "F6"])
+        expected = []
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        # release Fn key and all keys
+        r = uhdev.send_fn_state(0)
+        r.extend(uhdev.event([]))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+    def test_single_pageup_key_release_first(self):
+        """check for function key reliability with the [page] up key."""
+        uhdev = self.uhdev
+        evdev = uhdev.get_evdev()
+        syn_event = self.syn_event
+
+        r = uhdev.send_fn_state(1)
+        r.extend(uhdev.event(["UpArrow"]))
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 1))
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
+
+        r = uhdev.send_fn_state(0)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1
+        assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
+
+        r = uhdev.event([])
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 0))
+        events = uhdev.next_sync_events()
+        self.debug_reports(r, uhdev, events)
+        self.assertInputEventsIn(expected, events)
+        assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
+        assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 09/11] selftests: hid: import hid-tools hid-ite tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (5 preceding siblings ...)
  2023-02-17 16:18 ` [PATCH 08/11] selftests: hid: import hid-tools hid-apple tests Benjamin Tissoires
@ 2023-02-17 16:18 ` Benjamin Tissoires
  2023-02-17 16:18 ` [PATCH 10/11] selftests: hid: import hid-tools hid-sony and hid-playstation tests Benjamin Tissoires
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:18 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Peter Hutterer, Roderick Colenbrander

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: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile               |   1 +
 tools/testing/selftests/hid/config                 |   1 +
 tools/testing/selftests/hid/hid-ite.sh             |   7 +
 .../selftests/hid/tests/test_ite_keyboard.py       | 166 +++++++++++++++++++++
 4 files changed, 175 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index ce03c65bfba0..3ca696c44aab 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -8,6 +8,7 @@ include ../../../scripts/Makefile.include
 TEST_PROGS := hid-core.sh
 TEST_PROGS += hid-apple.sh
 TEST_PROGS += hid-gamepad.sh
+TEST_PROGS += hid-ite.sh
 TEST_PROGS += hid-keyboard.sh
 TEST_PROGS += hid-mouse.sh
 TEST_PROGS += hid-multitouch.sh
diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config
index 52b527cc2260..f400b8d94e3c 100644
--- a/tools/testing/selftests/hid/config
+++ b/tools/testing/selftests/hid/config
@@ -22,5 +22,6 @@ CONFIG_UHID=y
 CONFIG_USB=y
 CONFIG_USB_HID=y
 CONFIG_HID_APPLE=y
+CONFIG_HID_ITE=y
 CONFIG_HID_MULTITOUCH=y
 CONFIG_HID_WACOM=y
diff --git a/tools/testing/selftests/hid/hid-ite.sh b/tools/testing/selftests/hid/hid-ite.sh
new file mode 100755
index 000000000000..52c5ccf42292
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-ite.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_ite_keyboard.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_ite_keyboard.py b/tools/testing/selftests/hid/tests/test_ite_keyboard.py
new file mode 100644
index 000000000000..38550c167bae
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_ite_keyboard.py
@@ -0,0 +1,166 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2020 Red Hat, Inc.
+#
+
+from .test_keyboard import ArrayKeyboard, TestArrayKeyboard
+from hidtools.util import BusType
+
+import libevdev
+import logging
+
+logger = logging.getLogger("hidtools.test.ite-keyboard")
+
+KERNEL_MODULE = ("itetech", "hid_ite")
+
+
+class KbdData(object):
+    pass
+
+
+# The ITE keyboards have an issue regarding the Wifi key:
+# nothing comes in when pressing the key, but we get a null
+# event on the key release.
+# This test covers this case.
+class ITEKeyboard(ArrayKeyboard):
+    # fmt: off
+    report_descriptor = [
+        0x06, 0x85, 0xff,              # Usage Page (Vendor Usage Page 0xff85)
+        0x09, 0x95,                    # Usage (Vendor Usage 0x95)           3
+        0xa1, 0x01,                    # Collection (Application)            5
+        0x85, 0x5a,                    # .Report ID (90)                     7
+        0x09, 0x01,                    # .Usage (Vendor Usage 0x01)          9
+        0x15, 0x00,                    # .Logical Minimum (0)                11
+        0x26, 0xff, 0x00,              # .Logical Maximum (255)              13
+        0x75, 0x08,                    # .Report Size (8)                    16
+        0x95, 0x10,                    # .Report Count (16)                  18
+        0xb1, 0x00,                    # .Feature (Data,Arr,Abs)             20
+        0xc0,                          # End Collection                      22
+        0x05, 0x01,                    # Usage Page (Generic Desktop)        23
+        0x09, 0x06,                    # Usage (Keyboard)                    25
+        0xa1, 0x01,                    # Collection (Application)            27
+        0x85, 0x01,                    # .Report ID (1)                      29
+        0x75, 0x01,                    # .Report Size (1)                    31
+        0x95, 0x08,                    # .Report Count (8)                   33
+        0x05, 0x07,                    # .Usage Page (Keyboard)              35
+        0x19, 0xe0,                    # .Usage Minimum (224)                37
+        0x29, 0xe7,                    # .Usage Maximum (231)                39
+        0x15, 0x00,                    # .Logical Minimum (0)                41
+        0x25, 0x01,                    # .Logical Maximum (1)                43
+        0x81, 0x02,                    # .Input (Data,Var,Abs)               45
+        0x95, 0x01,                    # .Report Count (1)                   47
+        0x75, 0x08,                    # .Report Size (8)                    49
+        0x81, 0x03,                    # .Input (Cnst,Var,Abs)               51
+        0x95, 0x05,                    # .Report Count (5)                   53
+        0x75, 0x01,                    # .Report Size (1)                    55
+        0x05, 0x08,                    # .Usage Page (LEDs)                  57
+        0x19, 0x01,                    # .Usage Minimum (1)                  59
+        0x29, 0x05,                    # .Usage Maximum (5)                  61
+        0x91, 0x02,                    # .Output (Data,Var,Abs)              63
+        0x95, 0x01,                    # .Report Count (1)                   65
+        0x75, 0x03,                    # .Report Size (3)                    67
+        0x91, 0x03,                    # .Output (Cnst,Var,Abs)              69
+        0x95, 0x06,                    # .Report Count (6)                   71
+        0x75, 0x08,                    # .Report Size (8)                    73
+        0x15, 0x00,                    # .Logical Minimum (0)                75
+        0x26, 0xff, 0x00,              # .Logical Maximum (255)              77
+        0x05, 0x07,                    # .Usage Page (Keyboard)              80
+        0x19, 0x00,                    # .Usage Minimum (0)                  82
+        0x2a, 0xff, 0x00,              # .Usage Maximum (255)                84
+        0x81, 0x00,                    # .Input (Data,Arr,Abs)               87
+        0xc0,                          # End Collection                      89
+        0x05, 0x0c,                    # Usage Page (Consumer Devices)       90
+        0x09, 0x01,                    # Usage (Consumer Control)            92
+        0xa1, 0x01,                    # Collection (Application)            94
+        0x85, 0x02,                    # .Report ID (2)                      96
+        0x19, 0x00,                    # .Usage Minimum (0)                  98
+        0x2a, 0x3c, 0x02,              # .Usage Maximum (572)                100
+        0x15, 0x00,                    # .Logical Minimum (0)                103
+        0x26, 0x3c, 0x02,              # .Logical Maximum (572)              105
+        0x75, 0x10,                    # .Report Size (16)                   108
+        0x95, 0x01,                    # .Report Count (1)                   110
+        0x81, 0x00,                    # .Input (Data,Arr,Abs)               112
+        0xc0,                          # End Collection                      114
+        0x05, 0x01,                    # Usage Page (Generic Desktop)        115
+        0x09, 0x0c,                    # Usage (Wireless Radio Controls)     117
+        0xa1, 0x01,                    # Collection (Application)            119
+        0x85, 0x03,                    # .Report ID (3)                      121
+        0x15, 0x00,                    # .Logical Minimum (0)                123
+        0x25, 0x01,                    # .Logical Maximum (1)                125
+        0x09, 0xc6,                    # .Usage (Wireless Radio Button)      127
+        0x95, 0x01,                    # .Report Count (1)                   129
+        0x75, 0x01,                    # .Report Size (1)                    131
+        0x81, 0x06,                    # .Input (Data,Var,Rel)               133
+        0x75, 0x07,                    # .Report Size (7)                    135
+        0x81, 0x03,                    # .Input (Cnst,Var,Abs)               137
+        0xc0,                          # End Collection                      139
+        0x05, 0x88,                    # Usage Page (Vendor Usage Page 0x88) 140
+        0x09, 0x01,                    # Usage (Vendor Usage 0x01)           142
+        0xa1, 0x01,                    # Collection (Application)            144
+        0x85, 0x04,                    # .Report ID (4)                      146
+        0x19, 0x00,                    # .Usage Minimum (0)                  148
+        0x2a, 0xff, 0xff,              # .Usage Maximum (65535)              150
+        0x15, 0x00,                    # .Logical Minimum (0)                153
+        0x26, 0xff, 0xff,              # .Logical Maximum (65535)            155
+        0x75, 0x08,                    # .Report Size (8)                    158
+        0x95, 0x02,                    # .Report Count (2)                   160
+        0x81, 0x02,                    # .Input (Data,Var,Abs)               162
+        0xc0,                          # End Collection                      164
+        0x05, 0x01,                    # Usage Page (Generic Desktop)        165
+        0x09, 0x80,                    # Usage (System Control)              167
+        0xa1, 0x01,                    # Collection (Application)            169
+        0x85, 0x05,                    # .Report ID (5)                      171
+        0x19, 0x81,                    # .Usage Minimum (129)                173
+        0x29, 0x83,                    # .Usage Maximum (131)                175
+        0x15, 0x00,                    # .Logical Minimum (0)                177
+        0x25, 0x01,                    # .Logical Maximum (1)                179
+        0x95, 0x08,                    # .Report Count (8)                   181
+        0x75, 0x01,                    # .Report Size (1)                    183
+        0x81, 0x02,                    # .Input (Data,Var,Abs)               185
+        0xc0,                          # End Collection                      187
+    ]
+    # fmt: on
+
+    def __init__(
+        self,
+        rdesc=report_descriptor,
+        name=None,
+        input_info=(BusType.USB, 0x06CB, 0x2968),
+    ):
+        super().__init__(rdesc, name, input_info)
+
+    def event(self, keys, reportID=None, application=None):
+        application = application or "Keyboard"
+        return super().event(keys, reportID, application)
+
+
+class TestITEKeyboard(TestArrayKeyboard):
+    kernel_modules = [KERNEL_MODULE]
+
+    def create_device(self):
+        return ITEKeyboard()
+
+    def test_wifi_key(self):
+        uhdev = self.uhdev
+        syn_event = self.syn_event
+
+        # the following sends a 'release' event on the Wifi key.
+        # the kernel is supposed to translate this into Wifi key
+        # down and up
+        r = [0x03, 0x00]
+        uhdev.call_input_event(r)
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 1))
+        events = uhdev.next_sync_events()
+        self.debug_reports([r], uhdev, events)
+        self.assertInputEventsIn(expected, events)
+
+        expected = [syn_event]
+        expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 0))
+        # the kernel sends the two down/up key events in a batch, no need to
+        # call events = uhdev.next_sync_events()
+        self.debug_reports([], uhdev, events)
+        self.assertInputEventsIn(expected, events)

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 10/11] selftests: hid: import hid-tools hid-sony and hid-playstation tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (6 preceding siblings ...)
  2023-02-17 16:18 ` [PATCH 09/11] selftests: hid: import hid-tools hid-ite tests Benjamin Tissoires
@ 2023-02-17 16:18 ` Benjamin Tissoires
  2023-02-17 16:18 ` [PATCH 11/11] selftests: hid: import hid-tools usb-crash tests Benjamin Tissoires
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:18 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires,
	Roderick Colenbrander, Jose Torreguitar

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: Roderick Colenbrander <roderick.colenbrander@sony.com>
Cc: Jose Torreguitar <jtguitar@google.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile           |   1 +
 tools/testing/selftests/hid/config             |   5 +
 tools/testing/selftests/hid/hid-sony.sh        |   7 +
 tools/testing/selftests/hid/tests/test_sony.py | 282 +++++++++++++++++++++++++
 4 files changed, 295 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 3ca696c44aab..dcea4f1e9369 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -12,6 +12,7 @@ TEST_PROGS += hid-ite.sh
 TEST_PROGS += hid-keyboard.sh
 TEST_PROGS += hid-mouse.sh
 TEST_PROGS += hid-multitouch.sh
+TEST_PROGS += hid-sony.sh
 TEST_PROGS += hid-tablet.sh
 TEST_PROGS += hid-wacom.sh
 
diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config
index f400b8d94e3c..442a5ea16325 100644
--- a/tools/testing/selftests/hid/config
+++ b/tools/testing/selftests/hid/config
@@ -19,9 +19,14 @@ CONFIG_HIDRAW=y
 CONFIG_HID=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_UHID=y
+CONFIG_LEDS_CLASS_MULTICOLOR=y
 CONFIG_USB=y
 CONFIG_USB_HID=y
 CONFIG_HID_APPLE=y
 CONFIG_HID_ITE=y
 CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_PLAYSTATION=y
+CONFIG_PLAYSTATION_FF=y
+CONFIG_HID_SONY=y
+CONFIG_SONY_FF=y
 CONFIG_HID_WACOM=y
diff --git a/tools/testing/selftests/hid/hid-sony.sh b/tools/testing/selftests/hid/hid-sony.sh
new file mode 100755
index 000000000000..c863c442686e
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-sony.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_sony.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_sony.py b/tools/testing/selftests/hid/tests/test_sony.py
new file mode 100644
index 000000000000..c80f50ed29d3
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_sony.py
@@ -0,0 +1,282 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2020 Red Hat, Inc.
+#
+
+from .base import application_matches
+from .test_gamepad import BaseTest
+from hidtools.device.sony_gamepad import (
+    PS3Controller,
+    PS4ControllerBluetooth,
+    PS4ControllerUSB,
+    PS5ControllerBluetooth,
+    PS5ControllerUSB,
+    PSTouchPoint,
+)
+from hidtools.util import BusType
+
+import libevdev
+import logging
+import pytest
+
+logger = logging.getLogger("hidtools.test.sony")
+
+PS3_MODULE = ("sony", "hid_sony")
+PS4_MODULE = ("playstation", "hid_playstation")
+PS5_MODULE = ("playstation", "hid_playstation")
+
+
+class SonyBaseTest:
+    class SonyTest(BaseTest.TestGamepad):
+        pass
+
+    class SonyPS4ControllerTest(SonyTest):
+        kernel_modules = [PS4_MODULE]
+
+        def test_accelerometer(self):
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev("Accelerometer")
+
+            for x in range(-32000, 32000, 4000):
+                r = uhdev.event(accel=(x, None, None))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_X]
+                # Check against range due to small loss in precision due
+                # to inverse calibration, followed by calibration by hid-sony.
+                assert x - 1 <= value <= x + 1
+
+            for y in range(-32000, 32000, 4000):
+                r = uhdev.event(accel=(None, y, None))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_Y]
+                assert y - 1 <= value <= y + 1
+
+            for z in range(-32000, 32000, 4000):
+                r = uhdev.event(accel=(None, None, z))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_Z]
+                assert z - 1 <= value <= z + 1
+
+        def test_gyroscope(self):
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev("Accelerometer")
+
+            for rx in range(-2000000, 2000000, 200000):
+                r = uhdev.event(gyro=(rx, None, None))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_RX]
+                # Sensor internal value is 16-bit, but calibrated is 22-bit, so
+                # 6-bit (64) difference, so allow a range of +/- 64.
+                assert rx - 64 <= value <= rx + 64
+
+            for ry in range(-2000000, 2000000, 200000):
+                r = uhdev.event(gyro=(None, ry, None))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_RY]
+                assert ry - 64 <= value <= ry + 64
+
+            for rz in range(-2000000, 2000000, 200000):
+                r = uhdev.event(gyro=(None, None, rz))
+                events = uhdev.next_sync_events("Accelerometer")
+                self.debug_reports(r, uhdev, events)
+
+                assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events
+                value = evdev.value[libevdev.EV_ABS.ABS_RZ]
+                assert rz - 64 <= value <= rz + 64
+
+        def test_battery(self):
+            uhdev = self.uhdev
+
+            assert uhdev.power_supply_class is not None
+
+            # DS4 capacity levels are in increments of 10.
+            # Battery is never below 5%.
+            for i in range(5, 105, 10):
+                uhdev.battery.capacity = i
+                uhdev.event()
+                assert uhdev.power_supply_class.capacity == i
+
+            # Discharging tests only make sense for BlueTooth.
+            if uhdev.bus == BusType.BLUETOOTH:
+                uhdev.battery.cable_connected = False
+                uhdev.battery.capacity = 45
+                uhdev.event()
+                assert uhdev.power_supply_class.status == "Discharging"
+
+            uhdev.battery.cable_connected = True
+            uhdev.battery.capacity = 5
+            uhdev.event()
+            assert uhdev.power_supply_class.status == "Charging"
+
+            uhdev.battery.capacity = 100
+            uhdev.event()
+            assert uhdev.power_supply_class.status == "Charging"
+
+            uhdev.battery.full = True
+            uhdev.event()
+            assert uhdev.power_supply_class.status == "Full"
+
+        def test_mt_single_touch(self):
+            """send a single touch in the first slot of the device,
+            and release it."""
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev("Touch Pad")
+
+            t0 = PSTouchPoint(1, 50, 100)
+            r = uhdev.event(touch=[t0])
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+
+            assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
+
+            t0.tipswitch = False
+            r = uhdev.event(touch=[t0])
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+            assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+        def test_mt_dual_touch(self):
+            """Send 2 touches in the first 2 slots.
+            Make sure the kernel sees this as a dual touch.
+            Release and check
+
+            Note: PTP will send here BTN_DOUBLETAP emulation"""
+            uhdev = self.uhdev
+            evdev = uhdev.get_evdev("Touch Pad")
+
+            t0 = PSTouchPoint(1, 50, 100)
+            t1 = PSTouchPoint(2, 150, 200)
+
+            r = uhdev.event(touch=[t0])
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+
+            assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
+            assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+            r = uhdev.event(touch=[t0, t1])
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+            assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events
+            assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
+            assert (
+                libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events
+            )
+            assert (
+                libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events
+            )
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200
+
+            t0.tipswitch = False
+            r = uhdev.event(touch=[t0, t1])
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
+            assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events
+            assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events
+
+            t1.tipswitch = False
+            r = uhdev.event(touch=[t1])
+
+            events = uhdev.next_sync_events("Touch Pad")
+            self.debug_reports(r, uhdev, events)
+            assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+            assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+
+class TestPS3Controller(SonyBaseTest.SonyTest):
+    kernel_modules = [PS3_MODULE]
+
+    def create_device(self):
+        controller = PS3Controller()
+        controller.application_matches = application_matches
+        return controller
+
+    @pytest.fixture(autouse=True)
+    def start_controller(self):
+        # emulate a 'PS' button press to tell the kernel we are ready to accept events
+        self.assert_button(17)
+
+        # drain any remaining udev events
+        while self.uhdev.dispatch(10):
+            pass
+
+        def test_led(self):
+            for k, v in self.uhdev.led_classes.items():
+                # the kernel might have set a LED for us
+                logger.info(f"{k}: {v.brightness}")
+
+                idx = int(k[-1]) - 1
+                assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness)
+
+                v.brightness = 0
+                self.uhdev.dispatch(10)
+                assert self.uhdev.hw_leds.get_led(idx)[0] is False
+
+                v.brightness = v.max_brightness
+                self.uhdev.dispatch(10)
+                assert self.uhdev.hw_leds.get_led(idx)[0]
+
+
+class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
+    def create_device(self):
+        controller = PS4ControllerBluetooth()
+        controller.application_matches = application_matches
+        return controller
+
+
+class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
+    def create_device(self):
+        controller = PS4ControllerUSB()
+        controller.application_matches = application_matches
+        return controller
+
+
+class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
+    kernel_modules = [PS5_MODULE]
+
+    def create_device(self):
+        controller = PS5ControllerBluetooth()
+        controller.application_matches = application_matches
+        return controller
+
+
+class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
+    kernel_modules = [PS5_MODULE]
+
+    def create_device(self):
+        controller = PS5ControllerUSB()
+        controller.application_matches = application_matches
+        return controller

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 11/11] selftests: hid: import hid-tools usb-crash tests
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (7 preceding siblings ...)
  2023-02-17 16:18 ` [PATCH 10/11] selftests: hid: import hid-tools hid-sony and hid-playstation tests Benjamin Tissoires
@ 2023-02-17 16:18 ` Benjamin Tissoires
       [not found] ` <20230217-import-hid-tools-tests-v1-6-d1c48590d0ee@redhat.com>
  2023-04-03 16:20 ` [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
  10 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-02-17 16:18 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires

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.

This one gets skipped when run by vmtest.sh as we currently need to test
against actual kernel modules (.ko), not built-in to fetch the list
of supported devices.

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

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/Makefile               |   1 +
 tools/testing/selftests/hid/hid-usb_crash.sh       |   7 ++
 .../testing/selftests/hid/tests/test_usb_crash.py  | 103 +++++++++++++++++++++
 3 files changed, 111 insertions(+)

diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index dcea4f1e9369..01c0491d64da 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -14,6 +14,7 @@ TEST_PROGS += hid-mouse.sh
 TEST_PROGS += hid-multitouch.sh
 TEST_PROGS += hid-sony.sh
 TEST_PROGS += hid-tablet.sh
+TEST_PROGS += hid-usb_crash.sh
 TEST_PROGS += hid-wacom.sh
 
 CXX ?= $(CROSS_COMPILE)g++
diff --git a/tools/testing/selftests/hid/hid-usb_crash.sh b/tools/testing/selftests/hid/hid-usb_crash.sh
new file mode 100755
index 000000000000..3f0debe7e8fd
--- /dev/null
+++ b/tools/testing/selftests/hid/hid-usb_crash.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the HID subsystem
+
+export TARGET=test_usb_crash.py
+
+bash ./run-hid-tools-tests.sh
diff --git a/tools/testing/selftests/hid/tests/test_usb_crash.py b/tools/testing/selftests/hid/tests/test_usb_crash.py
new file mode 100644
index 000000000000..e98bff9197c7
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/test_usb_crash.py
@@ -0,0 +1,103 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+# Copyright (c) 2021 Red Hat, Inc.
+#
+
+# This is to ensure we don't crash when emulating USB devices
+
+from . import base
+import pytest
+import logging
+
+logger = logging.getLogger("hidtools.test.usb")
+
+
+class USBDev(base.UHIDTestDevice):
+    # fmt: off
+    report_descriptor = [
+        0x05, 0x01,  # .Usage Page (Generic Desktop)        0
+        0x09, 0x02,  # .Usage (Mouse)                       2
+        0xa1, 0x01,  # .Collection (Application)            4
+        0x09, 0x02,  # ..Usage (Mouse)                      6
+        0xa1, 0x02,  # ..Collection (Logical)               8
+        0x09, 0x01,  # ...Usage (Pointer)                   10
+        0xa1, 0x00,  # ...Collection (Physical)             12
+        0x05, 0x09,  # ....Usage Page (Button)              14
+        0x19, 0x01,  # ....Usage Minimum (1)                16
+        0x29, 0x03,  # ....Usage Maximum (3)                18
+        0x15, 0x00,  # ....Logical Minimum (0)              20
+        0x25, 0x01,  # ....Logical Maximum (1)              22
+        0x75, 0x01,  # ....Report Size (1)                  24
+        0x95, 0x03,  # ....Report Count (3)                 26
+        0x81, 0x02,  # ....Input (Data,Var,Abs)             28
+        0x75, 0x05,  # ....Report Size (5)                  30
+        0x95, 0x01,  # ....Report Count (1)                 32
+        0x81, 0x03,  # ....Input (Cnst,Var,Abs)             34
+        0x05, 0x01,  # ....Usage Page (Generic Desktop)     36
+        0x09, 0x30,  # ....Usage (X)                        38
+        0x09, 0x31,  # ....Usage (Y)                        40
+        0x15, 0x81,  # ....Logical Minimum (-127)           42
+        0x25, 0x7f,  # ....Logical Maximum (127)            44
+        0x75, 0x08,  # ....Report Size (8)                  46
+        0x95, 0x02,  # ....Report Count (2)                 48
+        0x81, 0x06,  # ....Input (Data,Var,Rel)             50
+        0xc0,        # ...End Collection                    52
+        0xc0,        # ..End Collection                     53
+        0xc0,        # .End Collection                      54
+    ]
+    # fmt: on
+
+    def __init__(self, name=None, input_info=None):
+        super().__init__(
+            name, "Mouse", input_info=input_info, rdesc=USBDev.report_descriptor
+        )
+
+    # skip witing for udev events, it's likely that the report
+    # descriptor is wrong
+    def is_ready(self):
+        return True
+
+    # we don't have an evdev node here, so paper over
+    # the checks
+    def get_evdev(self, application=None):
+        return "OK"
+
+
+class TestUSBDevice(base.BaseTestCase.TestUhid):
+    """
+    Test class to test if an emulated USB device crashes
+    the kernel.
+    """
+
+    # conftest.py is generating the following fixture:
+    #
+    # @pytest.fixture(params=[('modulename', 1, 2)])
+    # def usbVidPid(self, request):
+    #     return request.param
+
+    @pytest.fixture()
+    def new_uhdev(self, usbVidPid, request):
+        self.module, self.vid, self.pid = usbVidPid
+        self._load_kernel_module(None, self.module)
+        return USBDev(input_info=(3, self.vid, self.pid))
+
+    def test_creation(self):
+        """
+        inject the USB dev through uhid and immediately see if there is a crash:
+
+        uhid can create a USB device with the BUS_USB bus, and some
+        drivers assume that they can then access USB related structures
+        when they are actually provided a uhid device. This leads to
+        a crash because those access result in a segmentation fault.
+
+        The kernel should not crash on any (random) user space correct
+        use of its API. So run through all available modules and declared
+        devices to see if we can generate a uhid device without a crash.
+
+        The test is empty as the fixture `check_taint` is doing the job (and
+        honestly, when the kernel crashes, the whole machine freezes).
+        """
+        assert True

-- 
2.39.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 06/11] selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
       [not found] ` <20230217-import-hid-tools-tests-v1-6-d1c48590d0ee@redhat.com>
@ 2023-02-17 16:38   ` Ahelenia Ziemiańska
  0 siblings, 0 replies; 16+ messages in thread
From: Ahelenia Ziemiańska @ 2023-02-17 16:38 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Shuah Khan, linux-input, linux-kselftest,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1416 bytes --]

On Fri, Feb 17, 2023 at 05:18:00PM +0100, Benjamin Tissoires wrote:
> 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.
> 
> There are a lot of multitouch tests, and the default timeout of 45 seconds
> is not big enough. Bump it to 200 seconds.
> 
> [0] https://gitlab.freedesktop.org/libevdev/hid-tools
> 
> Cc: Peter Hutterer <peter.hutterer@who-t.net>
> Cc: Kai-Heng Feng <kai.heng.feng@canonical.com>
> Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
> Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
> Cc: Blaž Hrastnik <blaz@mxxn.io>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>  tools/testing/selftests/hid/Makefile               |    2 +
>  tools/testing/selftests/hid/config                 |    1 +
>  tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
>  tools/testing/selftests/hid/hid-tablet.sh          |    7 +
>  tools/testing/selftests/hid/settings               |    3 +
>  .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
>  tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
>  7 files changed, 2980 insertions(+)

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>

Best,

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests
  2023-02-17 16:17 ` [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests Benjamin Tissoires
@ 2023-02-18 20:24   ` Silvan Jegen
  0 siblings, 0 replies; 16+ messages in thread
From: Silvan Jegen @ 2023-02-18 20:24 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Shuah Khan, linux-input, linux-kselftest,
	linux-kernel, Candle Sun, Jose Torreguitar, Peter Hutterer,
	Roderick Colenbrander

Benjamin Tissoires <benjamin.tissoires@redhat.com> wrote:
> 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: Candle Sun <candle.sun@unisoc.com>
> Cc: Jose Torreguitar <jtguitar@google.com>
> Cc: Peter Hutterer <peter.hutterer@who-t.net>
> Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
> Cc: Silvan Jegen <s.jegen@gmail.com>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>  tools/testing/selftests/hid/Makefile              |   1 +
>  tools/testing/selftests/hid/hid-gamepad.sh        |   7 +
>  tools/testing/selftests/hid/tests/test_gamepad.py | 209 ++++++++++++++++++++++
>  3 files changed, 217 insertions(+)

It was only a one line change from my side but in any case.

Signed-off-by: Silvan Jegen <s.jegen@gmail.com>


> 
> diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
> index bdcb36d80c8c..d16a22477140 100644
> --- a/tools/testing/selftests/hid/Makefile
> +++ b/tools/testing/selftests/hid/Makefile
> @@ -6,6 +6,7 @@ include ../../../scripts/Makefile.arch
>  include ../../../scripts/Makefile.include
>  
>  TEST_PROGS := hid-core.sh
> +TEST_PROGS += hid-gamepad.sh
>  
>  CXX ?= $(CROSS_COMPILE)g++
>  
> diff --git a/tools/testing/selftests/hid/hid-gamepad.sh b/tools/testing/selftests/hid/hid-gamepad.sh
> new file mode 100755
> index 000000000000..1ba00c0ca95f
> --- /dev/null
> +++ b/tools/testing/selftests/hid/hid-gamepad.sh
> @@ -0,0 +1,7 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +# Runs tests for the HID subsystem
> +
> +export TARGET=test_gamepad.py
> +
> +bash ./run-hid-tools-tests.sh
> diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py
> new file mode 100644
> index 000000000000..26c74040b796
> --- /dev/null
> +++ b/tools/testing/selftests/hid/tests/test_gamepad.py
> @@ -0,0 +1,209 @@
> +#!/bin/env python3
> +# SPDX-License-Identifier: GPL-2.0
> +# -*- coding: utf-8 -*-
> +#
> +# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> +# Copyright (c) 2019 Red Hat, Inc.
> +#
> +
> +from . import base
> +import libevdev
> +import pytest
> +
> +from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad
> +
> +import logging
> +
> +logger = logging.getLogger("hidtools.test.gamepad")
> +
> +
> +class BaseTest:
> +    class TestGamepad(base.BaseTestCase.TestUhid):
> +        @pytest.fixture(autouse=True)
> +        def send_initial_state(self):
> +            """send an empty report to initialize the axes"""
> +            uhdev = self.uhdev
> +
> +            r = uhdev.event()
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +
> +        def assert_button(self, button):
> +            uhdev = self.uhdev
> +            evdev = uhdev.get_evdev()
> +            syn_event = self.syn_event
> +
> +            buttons = {}
> +            key = libevdev.evbit(uhdev.buttons_map[button])
> +
> +            buttons[button] = True
> +            r = uhdev.event(buttons=buttons)
> +            expected_event = libevdev.InputEvent(key, 1)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            self.assertInputEventsIn((syn_event, expected_event), events)
> +            assert evdev.value[key] == 1
> +
> +            buttons[button] = False
> +            r = uhdev.event(buttons=buttons)
> +            expected_event = libevdev.InputEvent(key, 0)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            self.assertInputEventsIn((syn_event, expected_event), events)
> +            assert evdev.value[key] == 0
> +
> +        def test_buttons(self):
> +            """check for button reliability."""
> +            uhdev = self.uhdev
> +
> +            for b in uhdev.buttons:
> +                self.assert_button(b)
> +
> +        def test_dual_buttons(self):
> +            """check for button reliability when pressing 2 buttons"""
> +            uhdev = self.uhdev
> +            evdev = uhdev.get_evdev()
> +            syn_event = self.syn_event
> +
> +            # can change intended b1 b2 values
> +            b1 = uhdev.buttons[0]
> +            key1 = libevdev.evbit(uhdev.buttons_map[b1])
> +            b2 = uhdev.buttons[1]
> +            key2 = libevdev.evbit(uhdev.buttons_map[b2])
> +
> +            buttons = {b1: True, b2: True}
> +            r = uhdev.event(buttons=buttons)
> +            expected_event0 = libevdev.InputEvent(key1, 1)
> +            expected_event1 = libevdev.InputEvent(key2, 1)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            self.assertInputEventsIn(
> +                (syn_event, expected_event0, expected_event1), events
> +            )
> +            assert evdev.value[key1] == 1
> +            assert evdev.value[key2] == 1
> +
> +            buttons = {b1: False, b2: None}
> +            r = uhdev.event(buttons=buttons)
> +            expected_event = libevdev.InputEvent(key1, 0)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            self.assertInputEventsIn((syn_event, expected_event), events)
> +            assert evdev.value[key1] == 0
> +            assert evdev.value[key2] == 1
> +
> +            buttons = {b1: None, b2: False}
> +            r = uhdev.event(buttons=buttons)
> +            expected_event = libevdev.InputEvent(key2, 0)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            self.assertInputEventsIn((syn_event, expected_event), events)
> +            assert evdev.value[key1] == 0
> +            assert evdev.value[key2] == 0
> +
> +        def _get_libevdev_abs_events(self, which):
> +            """Returns which ABS_* evdev axes are expected for the given stick"""
> +            abs_map = self.uhdev.axes_map[which]
> +
> +            x = abs_map["x"].evdev
> +            y = abs_map["y"].evdev
> +
> +            assert x
> +            assert y
> +
> +            return x, y
> +
> +        def _test_joystick_press(self, which, data):
> +            uhdev = self.uhdev
> +
> +            libevdev_axes = self._get_libevdev_abs_events(which)
> +
> +            r = None
> +            if which == "left_stick":
> +                r = uhdev.event(left=data)
> +            else:
> +                r = uhdev.event(right=data)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +
> +            for i, d in enumerate(data):
> +                if d is not None and d != 127:
> +                    assert libevdev.InputEvent(libevdev_axes[i], d) in events
> +                else:
> +                    assert libevdev.InputEvent(libevdev_axes[i]) not in events
> +
> +        def test_left_joystick_press_left(self):
> +            """check for the left joystick reliability"""
> +            self._test_joystick_press("left_stick", (63, None))
> +            self._test_joystick_press("left_stick", (0, 127))
> +
> +        def test_left_joystick_press_right(self):
> +            """check for the left joystick reliability"""
> +            self._test_joystick_press("left_stick", (191, 127))
> +            self._test_joystick_press("left_stick", (255, None))
> +
> +        def test_left_joystick_press_up(self):
> +            """check for the left joystick reliability"""
> +            self._test_joystick_press("left_stick", (None, 63))
> +            self._test_joystick_press("left_stick", (127, 0))
> +
> +        def test_left_joystick_press_down(self):
> +            """check for the left joystick reliability"""
> +            self._test_joystick_press("left_stick", (127, 191))
> +            self._test_joystick_press("left_stick", (None, 255))
> +
> +        def test_right_joystick_press_left(self):
> +            """check for the right joystick reliability"""
> +            self._test_joystick_press("right_stick", (63, None))
> +            self._test_joystick_press("right_stick", (0, 127))
> +
> +        def test_right_joystick_press_right(self):
> +            """check for the right joystick reliability"""
> +            self._test_joystick_press("right_stick", (191, 127))
> +            self._test_joystick_press("right_stick", (255, None))
> +
> +        def test_right_joystick_press_up(self):
> +            """check for the right joystick reliability"""
> +            self._test_joystick_press("right_stick", (None, 63))
> +            self._test_joystick_press("right_stick", (127, 0))
> +
> +        def test_right_joystick_press_down(self):
> +            """check for the right joystick reliability"""
> +            self._test_joystick_press("right_stick", (127, 191))
> +            self._test_joystick_press("right_stick", (None, 255))
> +
> +        @pytest.mark.skip_if_uhdev(
> +            lambda uhdev: "Hat switch" not in uhdev.fields,
> +            "Device not compatible, missing Hat switch usage",
> +        )
> +        @pytest.mark.parametrize(
> +            "hat_value,expected_evdev,evdev_value",
> +            [
> +                (0, "ABS_HAT0Y", -1),
> +                (2, "ABS_HAT0X", 1),
> +                (4, "ABS_HAT0Y", 1),
> +                (6, "ABS_HAT0X", -1),
> +            ],
> +        )
> +        def test_hat_switch(self, hat_value, expected_evdev, evdev_value):
> +            uhdev = self.uhdev
> +
> +            r = uhdev.event(hat_switch=hat_value)
> +            events = uhdev.next_sync_events()
> +            self.debug_reports(r, uhdev, events)
> +            assert (
> +                libevdev.InputEvent(
> +                    libevdev.evbit("EV_ABS", expected_evdev), evdev_value
> +                )
> +                in events
> +            )
> +
> +
> +class TestSaitekGamepad(BaseTest.TestGamepad):
> +    def create_device(self):
> +        return SaitekGamepad()
> +
> +
> +class TestAsusGamepad(BaseTest.TestGamepad):
> +    def create_device(self):
> +        return AsusGamepad()



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] selftests: hid: import the tests from hid-tools
  2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
                   ` (9 preceding siblings ...)
       [not found] ` <20230217-import-hid-tools-tests-v1-6-d1c48590d0ee@redhat.com>
@ 2023-04-03 16:20 ` Benjamin Tissoires
  2023-04-04  1:37   ` Peter Hutterer
  10 siblings, 1 reply; 16+ messages in thread
From: Benjamin Tissoires @ 2023-04-03 16:20 UTC (permalink / raw)
  To: Jiri Kosina, Shuah Khan
  Cc: linux-input, linux-kselftest, linux-kernel, Peter Hutterer,
	Candle Sun, Jose Torreguitar, Roderick Colenbrander, Silvan Jegen,
	Kai-Heng Feng, наб, Blaž Hrastnik,
	Jason Gerecke, Nicolas Saenz Julienne

On Feb 17 2023, Benjamin Tissoires wrote:
> I have been running hid-tools for a while, but it was in its own
> separate repository for multiple reasons. And the past few weeks
> I finally managed to make the kernel tests in that repo in a
> state where we can merge them in the kernel tree directly:
> 
> - the tests run in ~2 to 3 minutes
> - the tests are way more reliable than previously
> - the tests are mostly self-contained now (to the exception
>   of the Sony ones)
> 
> To be able to run the tests we need to use the latest release
> of hid-tools, as this project still keeps the HID parsing logic
> and is capable of generating the HID events.
> 
> The series also ensures we can run the tests with vmtest.sh,
> allowing for a quick development and test in the tree itself.
> 
> This should allow us to require tests to be added to a series
> when we see fit and keep them alive properly instead of having
> to deal with 2 repositories.
> 
> In Cc are all of the people who participated in the elaboration
> of those tests, so please send back a signed-off-by for each
> commit you are part of.
> 
> This series applies on top of the for-6.3/hid-bpf branch, which
> is the one that added the tools/testing/selftests/hid directory.
> Given that this is unlikely this series will make the cut for
> 6.3, we might just consider this series to be based on top of
> the future 6.3-rc1.
> 
> Cheers,
> Benjamin
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---

Jiri, do you mind if I push that code in the hid tree with the following
changes:
- Peter privately gave me his signed-off-by
- I included changes from https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143
  to fix the failing sony tests in v6.3

I am not a big fan of sending a v2 because the ML are not happy with the
amount of changes...

Cheers,
Benjamin

> Benjamin Tissoires (11):
>       selftests: hid: make vmtest rely on make
>       selftests: hid: import hid-tools hid-core tests
>       selftests: hid: import hid-tools hid-gamepad tests
>       selftests: hid: import hid-tools hid-keyboards tests
>       selftests: hid: import hid-tools hid-mouse tests
>       selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
>       selftests: hid: import hid-tools wacom tests
>       selftests: hid: import hid-tools hid-apple tests
>       selftests: hid: import hid-tools hid-ite tests
>       selftests: hid: import hid-tools hid-sony and hid-playstation tests
>       selftests: hid: import hid-tools usb-crash tests
> 
>  tools/testing/selftests/hid/Makefile               |   12 +
>  tools/testing/selftests/hid/config                 |   11 +
>  tools/testing/selftests/hid/hid-apple.sh           |    7 +
>  tools/testing/selftests/hid/hid-core.sh            |    7 +
>  tools/testing/selftests/hid/hid-gamepad.sh         |    7 +
>  tools/testing/selftests/hid/hid-ite.sh             |    7 +
>  tools/testing/selftests/hid/hid-keyboard.sh        |    7 +
>  tools/testing/selftests/hid/hid-mouse.sh           |    7 +
>  tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
>  tools/testing/selftests/hid/hid-sony.sh            |    7 +
>  tools/testing/selftests/hid/hid-tablet.sh          |    7 +
>  tools/testing/selftests/hid/hid-usb_crash.sh       |    7 +
>  tools/testing/selftests/hid/hid-wacom.sh           |    7 +
>  tools/testing/selftests/hid/run-hid-tools-tests.sh |   28 +
>  tools/testing/selftests/hid/settings               |    3 +
>  tools/testing/selftests/hid/tests/__init__.py      |    2 +
>  tools/testing/selftests/hid/tests/base.py          |  345 ++++
>  tools/testing/selftests/hid/tests/conftest.py      |   81 +
>  .../selftests/hid/tests/descriptors_wacom.py       | 1360 +++++++++++++
>  .../selftests/hid/tests/test_apple_keyboard.py     |  440 +++++
>  tools/testing/selftests/hid/tests/test_gamepad.py  |  209 ++
>  tools/testing/selftests/hid/tests/test_hid_core.py |  154 ++
>  .../selftests/hid/tests/test_ite_keyboard.py       |  166 ++
>  tools/testing/selftests/hid/tests/test_keyboard.py |  485 +++++
>  tools/testing/selftests/hid/tests/test_mouse.py    |  977 +++++++++
>  .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
>  tools/testing/selftests/hid/tests/test_sony.py     |  282 +++
>  tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
>  .../testing/selftests/hid/tests/test_usb_crash.py  |  103 +
>  .../selftests/hid/tests/test_wacom_generic.py      |  844 ++++++++
>  tools/testing/selftests/hid/vmtest.sh              |   25 +-
>  31 files changed, 8554 insertions(+), 10 deletions(-)
> ---
> base-commit: 2f7f4efb9411770b4ad99eb314d6418e980248b4
> change-id: 20230217-import-hid-tools-tests-dc0cd4f3c8a8
> 
> Best regards,
> -- 
> Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] selftests: hid: import the tests from hid-tools
  2023-04-03 16:20 ` [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
@ 2023-04-04  1:37   ` Peter Hutterer
  2023-04-04 23:22     ` Roderick Colenbrander
  0 siblings, 1 reply; 16+ messages in thread
From: Peter Hutterer @ 2023-04-04  1:37 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Shuah Khan, linux-input, linux-kselftest,
	linux-kernel, Candle Sun, Jose Torreguitar, Roderick Colenbrander,
	Silvan Jegen, Kai-Heng Feng, наб,
	Blaž Hrastnik, Jason Gerecke, Nicolas Saenz Julienne

On Mon, Apr 03, 2023 at 06:20:24PM +0200, Benjamin Tissoires wrote:
> On Feb 17 2023, Benjamin Tissoires wrote:
> > I have been running hid-tools for a while, but it was in its own
> > separate repository for multiple reasons. And the past few weeks
> > I finally managed to make the kernel tests in that repo in a
> > state where we can merge them in the kernel tree directly:
> > 
> > - the tests run in ~2 to 3 minutes
> > - the tests are way more reliable than previously
> > - the tests are mostly self-contained now (to the exception
> >   of the Sony ones)
> > 
> > To be able to run the tests we need to use the latest release
> > of hid-tools, as this project still keeps the HID parsing logic
> > and is capable of generating the HID events.
> > 
> > The series also ensures we can run the tests with vmtest.sh,
> > allowing for a quick development and test in the tree itself.
> > 
> > This should allow us to require tests to be added to a series
> > when we see fit and keep them alive properly instead of having
> > to deal with 2 repositories.
> > 
> > In Cc are all of the people who participated in the elaboration
> > of those tests, so please send back a signed-off-by for each
> > commit you are part of.
> > 
> > This series applies on top of the for-6.3/hid-bpf branch, which
> > is the one that added the tools/testing/selftests/hid directory.
> > Given that this is unlikely this series will make the cut for
> > 6.3, we might just consider this series to be based on top of
> > the future 6.3-rc1.
> > 
> > Cheers,
> > Benjamin
> > 
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > ---
> 
> Jiri, do you mind if I push that code in the hid tree with the following
> changes:
> - Peter privately gave me his signed-off-by

Apologies, this fell off my list after the initial ack in a meeting with
Benjamin. This time publicly:
  Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
for the relevant commits.

Cheers,
  Peter


> - I included changes from https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143
>   to fix the failing sony tests in v6.3
> 
> I am not a big fan of sending a v2 because the ML are not happy with the
> amount of changes...
> 
> Cheers,
> Benjamin
> 
> > Benjamin Tissoires (11):
> >       selftests: hid: make vmtest rely on make
> >       selftests: hid: import hid-tools hid-core tests
> >       selftests: hid: import hid-tools hid-gamepad tests
> >       selftests: hid: import hid-tools hid-keyboards tests
> >       selftests: hid: import hid-tools hid-mouse tests
> >       selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
> >       selftests: hid: import hid-tools wacom tests
> >       selftests: hid: import hid-tools hid-apple tests
> >       selftests: hid: import hid-tools hid-ite tests
> >       selftests: hid: import hid-tools hid-sony and hid-playstation tests
> >       selftests: hid: import hid-tools usb-crash tests
> > 
> >  tools/testing/selftests/hid/Makefile               |   12 +
> >  tools/testing/selftests/hid/config                 |   11 +
> >  tools/testing/selftests/hid/hid-apple.sh           |    7 +
> >  tools/testing/selftests/hid/hid-core.sh            |    7 +
> >  tools/testing/selftests/hid/hid-gamepad.sh         |    7 +
> >  tools/testing/selftests/hid/hid-ite.sh             |    7 +
> >  tools/testing/selftests/hid/hid-keyboard.sh        |    7 +
> >  tools/testing/selftests/hid/hid-mouse.sh           |    7 +
> >  tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
> >  tools/testing/selftests/hid/hid-sony.sh            |    7 +
> >  tools/testing/selftests/hid/hid-tablet.sh          |    7 +
> >  tools/testing/selftests/hid/hid-usb_crash.sh       |    7 +
> >  tools/testing/selftests/hid/hid-wacom.sh           |    7 +
> >  tools/testing/selftests/hid/run-hid-tools-tests.sh |   28 +
> >  tools/testing/selftests/hid/settings               |    3 +
> >  tools/testing/selftests/hid/tests/__init__.py      |    2 +
> >  tools/testing/selftests/hid/tests/base.py          |  345 ++++
> >  tools/testing/selftests/hid/tests/conftest.py      |   81 +
> >  .../selftests/hid/tests/descriptors_wacom.py       | 1360 +++++++++++++
> >  .../selftests/hid/tests/test_apple_keyboard.py     |  440 +++++
> >  tools/testing/selftests/hid/tests/test_gamepad.py  |  209 ++
> >  tools/testing/selftests/hid/tests/test_hid_core.py |  154 ++
> >  .../selftests/hid/tests/test_ite_keyboard.py       |  166 ++
> >  tools/testing/selftests/hid/tests/test_keyboard.py |  485 +++++
> >  tools/testing/selftests/hid/tests/test_mouse.py    |  977 +++++++++
> >  .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
> >  tools/testing/selftests/hid/tests/test_sony.py     |  282 +++
> >  tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
> >  .../testing/selftests/hid/tests/test_usb_crash.py  |  103 +
> >  .../selftests/hid/tests/test_wacom_generic.py      |  844 ++++++++
> >  tools/testing/selftests/hid/vmtest.sh              |   25 +-
> >  31 files changed, 8554 insertions(+), 10 deletions(-)
> > ---
> > base-commit: 2f7f4efb9411770b4ad99eb314d6418e980248b4
> > change-id: 20230217-import-hid-tools-tests-dc0cd4f3c8a8
> > 
> > Best regards,
> > -- 
> > Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] selftests: hid: import the tests from hid-tools
  2023-04-04  1:37   ` Peter Hutterer
@ 2023-04-04 23:22     ` Roderick Colenbrander
  2023-04-12 15:18       ` Benjamin Tissoires
  0 siblings, 1 reply; 16+ messages in thread
From: Roderick Colenbrander @ 2023-04-04 23:22 UTC (permalink / raw)
  To: Peter Hutterer
  Cc: Benjamin Tissoires, Jiri Kosina, Shuah Khan, linux-input,
	linux-kselftest, linux-kernel, Candle Sun, Jose Torreguitar,
	Roderick Colenbrander, Silvan Jegen, Kai-Heng Feng,
	наб, Blaž Hrastnik, Jason Gerecke,
	Nicolas Saenz Julienne

Hi Benjamin,

I like the direction of bundling the tests with the kernel and should
make it easier in case there are driver changes breaking tests as
well.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>

Thanks,
Roderick Colenbrander

On Mon, Apr 3, 2023 at 6:54 PM Peter Hutterer <peter.hutterer@who-t.net> wrote:
>
> On Mon, Apr 03, 2023 at 06:20:24PM +0200, Benjamin Tissoires wrote:
> > On Feb 17 2023, Benjamin Tissoires wrote:
> > > I have been running hid-tools for a while, but it was in its own
> > > separate repository for multiple reasons. And the past few weeks
> > > I finally managed to make the kernel tests in that repo in a
> > > state where we can merge them in the kernel tree directly:
> > >
> > > - the tests run in ~2 to 3 minutes
> > > - the tests are way more reliable than previously
> > > - the tests are mostly self-contained now (to the exception
> > >   of the Sony ones)
> > >
> > > To be able to run the tests we need to use the latest release
> > > of hid-tools, as this project still keeps the HID parsing logic
> > > and is capable of generating the HID events.
> > >
> > > The series also ensures we can run the tests with vmtest.sh,
> > > allowing for a quick development and test in the tree itself.
> > >
> > > This should allow us to require tests to be added to a series
> > > when we see fit and keep them alive properly instead of having
> > > to deal with 2 repositories.
> > >
> > > In Cc are all of the people who participated in the elaboration
> > > of those tests, so please send back a signed-off-by for each
> > > commit you are part of.
> > >
> > > This series applies on top of the for-6.3/hid-bpf branch, which
> > > is the one that added the tools/testing/selftests/hid directory.
> > > Given that this is unlikely this series will make the cut for
> > > 6.3, we might just consider this series to be based on top of
> > > the future 6.3-rc1.
> > >
> > > Cheers,
> > > Benjamin
> > >
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > ---
> >
> > Jiri, do you mind if I push that code in the hid tree with the following
> > changes:
> > - Peter privately gave me his signed-off-by
>
> Apologies, this fell off my list after the initial ack in a meeting with
> Benjamin. This time publicly:
>   Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> for the relevant commits.
>
> Cheers,
>   Peter
>
>
> > - I included changes from https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143
> >   to fix the failing sony tests in v6.3
> >
> > I am not a big fan of sending a v2 because the ML are not happy with the
> > amount of changes...
> >
> > Cheers,
> > Benjamin
> >
> > > Benjamin Tissoires (11):
> > >       selftests: hid: make vmtest rely on make
> > >       selftests: hid: import hid-tools hid-core tests
> > >       selftests: hid: import hid-tools hid-gamepad tests
> > >       selftests: hid: import hid-tools hid-keyboards tests
> > >       selftests: hid: import hid-tools hid-mouse tests
> > >       selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
> > >       selftests: hid: import hid-tools wacom tests
> > >       selftests: hid: import hid-tools hid-apple tests
> > >       selftests: hid: import hid-tools hid-ite tests
> > >       selftests: hid: import hid-tools hid-sony and hid-playstation tests
> > >       selftests: hid: import hid-tools usb-crash tests
> > >
> > >  tools/testing/selftests/hid/Makefile               |   12 +
> > >  tools/testing/selftests/hid/config                 |   11 +
> > >  tools/testing/selftests/hid/hid-apple.sh           |    7 +
> > >  tools/testing/selftests/hid/hid-core.sh            |    7 +
> > >  tools/testing/selftests/hid/hid-gamepad.sh         |    7 +
> > >  tools/testing/selftests/hid/hid-ite.sh             |    7 +
> > >  tools/testing/selftests/hid/hid-keyboard.sh        |    7 +
> > >  tools/testing/selftests/hid/hid-mouse.sh           |    7 +
> > >  tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
> > >  tools/testing/selftests/hid/hid-sony.sh            |    7 +
> > >  tools/testing/selftests/hid/hid-tablet.sh          |    7 +
> > >  tools/testing/selftests/hid/hid-usb_crash.sh       |    7 +
> > >  tools/testing/selftests/hid/hid-wacom.sh           |    7 +
> > >  tools/testing/selftests/hid/run-hid-tools-tests.sh |   28 +
> > >  tools/testing/selftests/hid/settings               |    3 +
> > >  tools/testing/selftests/hid/tests/__init__.py      |    2 +
> > >  tools/testing/selftests/hid/tests/base.py          |  345 ++++
> > >  tools/testing/selftests/hid/tests/conftest.py      |   81 +
> > >  .../selftests/hid/tests/descriptors_wacom.py       | 1360 +++++++++++++
> > >  .../selftests/hid/tests/test_apple_keyboard.py     |  440 +++++
> > >  tools/testing/selftests/hid/tests/test_gamepad.py  |  209 ++
> > >  tools/testing/selftests/hid/tests/test_hid_core.py |  154 ++
> > >  .../selftests/hid/tests/test_ite_keyboard.py       |  166 ++
> > >  tools/testing/selftests/hid/tests/test_keyboard.py |  485 +++++
> > >  tools/testing/selftests/hid/tests/test_mouse.py    |  977 +++++++++
> > >  .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
> > >  tools/testing/selftests/hid/tests/test_sony.py     |  282 +++
> > >  tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
> > >  .../testing/selftests/hid/tests/test_usb_crash.py  |  103 +
> > >  .../selftests/hid/tests/test_wacom_generic.py      |  844 ++++++++
> > >  tools/testing/selftests/hid/vmtest.sh              |   25 +-
> > >  31 files changed, 8554 insertions(+), 10 deletions(-)
> > > ---
> > > base-commit: 2f7f4efb9411770b4ad99eb314d6418e980248b4
> > > change-id: 20230217-import-hid-tools-tests-dc0cd4f3c8a8
> > >
> > > Best regards,
> > > --
> > > Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> >

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] selftests: hid: import the tests from hid-tools
  2023-04-04 23:22     ` Roderick Colenbrander
@ 2023-04-12 15:18       ` Benjamin Tissoires
  0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Tissoires @ 2023-04-12 15:18 UTC (permalink / raw)
  To: Roderick Colenbrander
  Cc: Peter Hutterer, Jiri Kosina, Shuah Khan, linux-input,
	linux-kselftest, linux-kernel, Candle Sun, Jose Torreguitar,
	Roderick Colenbrander, Silvan Jegen, Kai-Heng Feng,
	наб, Blaž Hrastnik, Jason Gerecke,
	Nicolas Saenz Julienne

On Wed, Apr 5, 2023 at 1:22 AM Roderick Colenbrander
<thunderbird2k@gmail.com> wrote:
>
> Hi Benjamin,
>
> I like the direction of bundling the tests with the kernel and should
> make it easier in case there are driver changes breaking tests as
> well.
>
> Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>

Thanks everybody.

I have now pushed this series with the sony fixes from hid-tools in
the branch for-6.4/tests, in the hid tree.

Cheers,
Benjamin

>
> Thanks,
> Roderick Colenbrander
>
> On Mon, Apr 3, 2023 at 6:54 PM Peter Hutterer <peter.hutterer@who-t.net> wrote:
> >
> > On Mon, Apr 03, 2023 at 06:20:24PM +0200, Benjamin Tissoires wrote:
> > > On Feb 17 2023, Benjamin Tissoires wrote:
> > > > I have been running hid-tools for a while, but it was in its own
> > > > separate repository for multiple reasons. And the past few weeks
> > > > I finally managed to make the kernel tests in that repo in a
> > > > state where we can merge them in the kernel tree directly:
> > > >
> > > > - the tests run in ~2 to 3 minutes
> > > > - the tests are way more reliable than previously
> > > > - the tests are mostly self-contained now (to the exception
> > > >   of the Sony ones)
> > > >
> > > > To be able to run the tests we need to use the latest release
> > > > of hid-tools, as this project still keeps the HID parsing logic
> > > > and is capable of generating the HID events.
> > > >
> > > > The series also ensures we can run the tests with vmtest.sh,
> > > > allowing for a quick development and test in the tree itself.
> > > >
> > > > This should allow us to require tests to be added to a series
> > > > when we see fit and keep them alive properly instead of having
> > > > to deal with 2 repositories.
> > > >
> > > > In Cc are all of the people who participated in the elaboration
> > > > of those tests, so please send back a signed-off-by for each
> > > > commit you are part of.
> > > >
> > > > This series applies on top of the for-6.3/hid-bpf branch, which
> > > > is the one that added the tools/testing/selftests/hid directory.
> > > > Given that this is unlikely this series will make the cut for
> > > > 6.3, we might just consider this series to be based on top of
> > > > the future 6.3-rc1.
> > > >
> > > > Cheers,
> > > > Benjamin
> > > >
> > > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > > ---
> > >
> > > Jiri, do you mind if I push that code in the hid tree with the following
> > > changes:
> > > - Peter privately gave me his signed-off-by
> >
> > Apologies, this fell off my list after the initial ack in a meeting with
> > Benjamin. This time publicly:
> >   Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> > for the relevant commits.
> >
> > Cheers,
> >   Peter
> >
> >
> > > - I included changes from https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143
> > >   to fix the failing sony tests in v6.3
> > >
> > > I am not a big fan of sending a v2 because the ML are not happy with the
> > > amount of changes...
> > >
> > > Cheers,
> > > Benjamin
> > >
> > > > Benjamin Tissoires (11):
> > > >       selftests: hid: make vmtest rely on make
> > > >       selftests: hid: import hid-tools hid-core tests
> > > >       selftests: hid: import hid-tools hid-gamepad tests
> > > >       selftests: hid: import hid-tools hid-keyboards tests
> > > >       selftests: hid: import hid-tools hid-mouse tests
> > > >       selftests: hid: import hid-tools hid-multitouch and hid-tablets tests
> > > >       selftests: hid: import hid-tools wacom tests
> > > >       selftests: hid: import hid-tools hid-apple tests
> > > >       selftests: hid: import hid-tools hid-ite tests
> > > >       selftests: hid: import hid-tools hid-sony and hid-playstation tests
> > > >       selftests: hid: import hid-tools usb-crash tests
> > > >
> > > >  tools/testing/selftests/hid/Makefile               |   12 +
> > > >  tools/testing/selftests/hid/config                 |   11 +
> > > >  tools/testing/selftests/hid/hid-apple.sh           |    7 +
> > > >  tools/testing/selftests/hid/hid-core.sh            |    7 +
> > > >  tools/testing/selftests/hid/hid-gamepad.sh         |    7 +
> > > >  tools/testing/selftests/hid/hid-ite.sh             |    7 +
> > > >  tools/testing/selftests/hid/hid-keyboard.sh        |    7 +
> > > >  tools/testing/selftests/hid/hid-mouse.sh           |    7 +
> > > >  tools/testing/selftests/hid/hid-multitouch.sh      |    7 +
> > > >  tools/testing/selftests/hid/hid-sony.sh            |    7 +
> > > >  tools/testing/selftests/hid/hid-tablet.sh          |    7 +
> > > >  tools/testing/selftests/hid/hid-usb_crash.sh       |    7 +
> > > >  tools/testing/selftests/hid/hid-wacom.sh           |    7 +
> > > >  tools/testing/selftests/hid/run-hid-tools-tests.sh |   28 +
> > > >  tools/testing/selftests/hid/settings               |    3 +
> > > >  tools/testing/selftests/hid/tests/__init__.py      |    2 +
> > > >  tools/testing/selftests/hid/tests/base.py          |  345 ++++
> > > >  tools/testing/selftests/hid/tests/conftest.py      |   81 +
> > > >  .../selftests/hid/tests/descriptors_wacom.py       | 1360 +++++++++++++
> > > >  .../selftests/hid/tests/test_apple_keyboard.py     |  440 +++++
> > > >  tools/testing/selftests/hid/tests/test_gamepad.py  |  209 ++
> > > >  tools/testing/selftests/hid/tests/test_hid_core.py |  154 ++
> > > >  .../selftests/hid/tests/test_ite_keyboard.py       |  166 ++
> > > >  tools/testing/selftests/hid/tests/test_keyboard.py |  485 +++++
> > > >  tools/testing/selftests/hid/tests/test_mouse.py    |  977 +++++++++
> > > >  .../testing/selftests/hid/tests/test_multitouch.py | 2088 ++++++++++++++++++++
> > > >  tools/testing/selftests/hid/tests/test_sony.py     |  282 +++
> > > >  tools/testing/selftests/hid/tests/test_tablet.py   |  872 ++++++++
> > > >  .../testing/selftests/hid/tests/test_usb_crash.py  |  103 +
> > > >  .../selftests/hid/tests/test_wacom_generic.py      |  844 ++++++++
> > > >  tools/testing/selftests/hid/vmtest.sh              |   25 +-
> > > >  31 files changed, 8554 insertions(+), 10 deletions(-)
> > > > ---
> > > > base-commit: 2f7f4efb9411770b4ad99eb314d6418e980248b4
> > > > change-id: 20230217-import-hid-tools-tests-dc0cd4f3c8a8
> > > >
> > > > Best regards,
> > > > --
> > > > Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > >
> > >
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2023-04-12 15:19 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-17 16:17 [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
2023-02-17 16:17 ` [PATCH 01/11] selftests: hid: make vmtest rely on make Benjamin Tissoires
2023-02-17 16:17 ` [PATCH 02/11] selftests: hid: import hid-tools hid-core tests Benjamin Tissoires
2023-02-17 16:17 ` [PATCH 03/11] selftests: hid: import hid-tools hid-gamepad tests Benjamin Tissoires
2023-02-18 20:24   ` Silvan Jegen
2023-02-17 16:17 ` [PATCH 04/11] selftests: hid: import hid-tools hid-keyboards tests Benjamin Tissoires
2023-02-17 16:17 ` [PATCH 05/11] selftests: hid: import hid-tools hid-mouse tests Benjamin Tissoires
2023-02-17 16:18 ` [PATCH 08/11] selftests: hid: import hid-tools hid-apple tests Benjamin Tissoires
2023-02-17 16:18 ` [PATCH 09/11] selftests: hid: import hid-tools hid-ite tests Benjamin Tissoires
2023-02-17 16:18 ` [PATCH 10/11] selftests: hid: import hid-tools hid-sony and hid-playstation tests Benjamin Tissoires
2023-02-17 16:18 ` [PATCH 11/11] selftests: hid: import hid-tools usb-crash tests Benjamin Tissoires
     [not found] ` <20230217-import-hid-tools-tests-v1-6-d1c48590d0ee@redhat.com>
2023-02-17 16:38   ` [PATCH 06/11] selftests: hid: import hid-tools hid-multitouch and hid-tablets tests Ahelenia Ziemiańska
2023-04-03 16:20 ` [PATCH 00/11] selftests: hid: import the tests from hid-tools Benjamin Tissoires
2023-04-04  1:37   ` Peter Hutterer
2023-04-04 23:22     ` Roderick Colenbrander
2023-04-12 15:18       ` Benjamin Tissoires

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).