From nobody Mon Feb 9 09:38:31 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9B12C4167B for ; Wed, 29 Nov 2023 15:25:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234717AbjK2PZW (ORCPT ); Wed, 29 Nov 2023 10:25:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54556 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234929AbjK2PZG (ORCPT ); Wed, 29 Nov 2023 10:25:06 -0500 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9FCC3C9 for ; Wed, 29 Nov 2023 07:25:11 -0800 (PST) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BFD61C433CB; Wed, 29 Nov 2023 15:25:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1701271511; bh=FgvDYTZPmqNfjQj10BxzNHCLREarvkKj4LedIEIJzdE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=eHzkmC9Ba0CB32ONG92dG26xVvf/wdMxIuSWquwWnsm0zTLr4zU8edPQtLqZGuu3t u9BgzP1CRgDG46RqPnNeX/SWOMIPbrfO5KEvt9xOdnu9uurXOvtMqWsSH0YRHQ0hB4 xJnXJNmvWyFvrCVXYb46E9jWaVhFoBHAZCVc/fhTeUXjQjopdsfI7rrMxdqrNvqBGK SBSbUEaLWDqWGUtR9juaMV1pIzhqwLV11oB8zAXw2hRTdCrkiRTiJs4NjDPYmC6qU1 1gYaFleLcdxyIRVBMm6YuEDRs9w8Oa5aw9eTCJ3I/rIlcl6Grn6cgoyNFWYKqpBcRF Z3tOV7nORK7JQ== From: Benjamin Tissoires Date: Wed, 29 Nov 2023 16:24:30 +0100 Subject: [PATCH 05/12] selftests/hid: tablets: move the transitions to PenState MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20231129-wip-selftests-v1-5-ba15a1fe1b0d@kernel.org> References: <20231129-wip-selftests-v1-0-ba15a1fe1b0d@kernel.org> In-Reply-To: <20231129-wip-selftests-v1-0-ba15a1fe1b0d@kernel.org> To: Jiri Kosina , Benjamin Tissoires , Shuah Khan , Peter Hutterer Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.12.4 X-Developer-Signature: v=1; a=ed25519-sha256; t=1701271499; l=11653; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=FgvDYTZPmqNfjQj10BxzNHCLREarvkKj4LedIEIJzdE=; b=rqb4WviI1/OlPiyv8gdgY4LjcMWP4CqgreEQ5imq4IL4Gu7KVDcfT8eI653jGSOtBEdFdfY92 bVJXYFNFkBbAXXL0y1FZErxkblWGmyc7CMTE1qY78nEaLhzIJjPnqhz X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Those transitions have nothing to do with `Pen`, so migrate them to `PenState`. The hidden agenda is to remove `Pen` and integrate it into `PenDigitizer` so that we can tweak the events in each state to emulate firmware bugs. Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/tests/test_tablet.py | 212 +++++++++++--------= ---- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testi= ng/selftests/hid/tests/test_tablet.py index cd9c1269afa6..18961758e4aa 100644 --- a/tools/testing/selftests/hid/tests/test_tablet.py +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -132,104 +132,8 @@ class PenState(Enum): =20 return tuple() =20 - -class Pen(object): - def __init__(self, x, y): - self.x =3D x - self.y =3D y - self.tipswitch =3D False - self.tippressure =3D 15 - self.azimuth =3D 0 - self.inrange =3D False - self.width =3D 10 - self.height =3D 10 - self.barrelswitch =3D False - self.invert =3D False - self.eraser =3D False - self.x_tilt =3D 0 - self.y_tilt =3D 0 - self.twist =3D 0 - self._old_values =3D None - self.current_state =3D None - - def _restore(self): - if self._old_values is not None: - for i in [ - "x", - "y", - "tippressure", - "azimuth", - "width", - "height", - "twist", - "x_tilt", - "y_tilt", - ]: - setattr(self, i, getattr(self._old_values, i)) - - def move_to(self, state): - # fill in the previous values - if self.current_state =3D=3D PenState.PEN_IS_OUT_OF_RANGE: - self._restore() - - print(f"\n *** pen is moving to {state} ***") - - if state =3D=3D PenState.PEN_IS_OUT_OF_RANGE: - self._old_values =3D copy.copy(self) - self.x =3D 0 - self.y =3D 0 - self.tipswitch =3D False - self.tippressure =3D 0 - self.azimuth =3D 0 - self.inrange =3D False - self.width =3D 0 - self.height =3D 0 - self.invert =3D False - self.eraser =3D False - self.x_tilt =3D 0 - self.y_tilt =3D 0 - self.twist =3D 0 - elif state =3D=3D PenState.PEN_IS_IN_RANGE: - self.tipswitch =3D False - self.inrange =3D True - self.invert =3D False - self.eraser =3D False - elif state =3D=3D PenState.PEN_IS_IN_CONTACT: - self.tipswitch =3D True - self.inrange =3D True - self.invert =3D False - self.eraser =3D False - elif state =3D=3D PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: - self.tipswitch =3D False - self.inrange =3D True - self.invert =3D True - self.eraser =3D False - elif state =3D=3D PenState.PEN_IS_ERASING: - self.tipswitch =3D False - self.inrange =3D True - self.invert =3D True - self.eraser =3D True - - self.current_state =3D state - - def __assert_axis(self, evdev, axis, value): - if ( - axis =3D=3D libevdev.EV_KEY.BTN_TOOL_RUBBER - and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None - ): - return - - assert ( - evdev.value[axis] =3D=3D value - ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) !=3D {value}" - - def assert_expected_input_events(self, evdev): - assert evdev.value[libevdev.EV_ABS.ABS_X] =3D=3D self.x - assert evdev.value[libevdev.EV_ABS.ABS_Y] =3D=3D self.y - assert self.current_state =3D=3D PenState.from_evdev(evdev) - @staticmethod - def legal_transitions() -> Dict[str, Tuple[PenState, ...]]: + def legal_transitions() -> Dict[str, Tuple["PenState", ...]]: """This is the first half of the Windows Pen Implementation state = machine: we don't have Invert nor Erase bits, so just move in/out-of-range = or proximity. https://docs.microsoft.com/en-us/windows-hardware/design/component= -guidelines/windows-pen-states @@ -255,7 +159,7 @@ class Pen(object): } =20 @staticmethod - def legal_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]: + def legal_transitions_with_invert() -> Dict[str, Tuple["PenState", ...= ]]: """This is the second half of the Windows Pen Implementation state= machine: we now have Invert and Erase bits, so move in/out or proximity wit= h the intend to erase. @@ -293,7 +197,7 @@ class Pen(object): } =20 @staticmethod - def tolerated_transitions() -> Dict[str, Tuple[PenState, ...]]: + def tolerated_transitions() -> Dict[str, Tuple["PenState", ...]]: """This is not adhering to the Windows Pen Implementation state ma= chine but we should expect the kernel to behave properly, mostly for his= torical reasons.""" @@ -306,7 +210,7 @@ class Pen(object): } =20 @staticmethod - def tolerated_transitions_with_invert() -> Dict[str, Tuple[PenState, .= ..]]: + def tolerated_transitions_with_invert() -> Dict[str, Tuple["PenState",= ...]]: """This is the second half of the Windows Pen Implementation state= machine: we now have Invert and Erase bits, so move in/out or proximity wit= h the intend to erase. @@ -321,7 +225,7 @@ class Pen(object): } =20 @staticmethod - def broken_transitions() -> Dict[str, Tuple[PenState, ...]]: + def broken_transitions() -> Dict[str, Tuple["PenState", ...]]: """Those tests are definitely not part of the Windows specificatio= n. However, a half broken device might export those transitions. For example, a pen that has the eraser button might wobble between @@ -359,6 +263,102 @@ class Pen(object): } =20 =20 +class Pen(object): + def __init__(self, x, y): + self.x =3D x + self.y =3D y + self.tipswitch =3D False + self.tippressure =3D 15 + self.azimuth =3D 0 + self.inrange =3D False + self.width =3D 10 + self.height =3D 10 + self.barrelswitch =3D False + self.invert =3D False + self.eraser =3D False + self.x_tilt =3D 0 + self.y_tilt =3D 0 + self.twist =3D 0 + self._old_values =3D None + self.current_state =3D None + + def _restore(self): + if self._old_values is not None: + for i in [ + "x", + "y", + "tippressure", + "azimuth", + "width", + "height", + "twist", + "x_tilt", + "y_tilt", + ]: + setattr(self, i, getattr(self._old_values, i)) + + def move_to(self, state): + # fill in the previous values + if self.current_state =3D=3D PenState.PEN_IS_OUT_OF_RANGE: + self._restore() + + print(f"\n *** pen is moving to {state} ***") + + if state =3D=3D PenState.PEN_IS_OUT_OF_RANGE: + self._old_values =3D copy.copy(self) + self.x =3D 0 + self.y =3D 0 + self.tipswitch =3D False + self.tippressure =3D 0 + self.azimuth =3D 0 + self.inrange =3D False + self.width =3D 0 + self.height =3D 0 + self.invert =3D False + self.eraser =3D False + self.x_tilt =3D 0 + self.y_tilt =3D 0 + self.twist =3D 0 + elif state =3D=3D PenState.PEN_IS_IN_RANGE: + self.tipswitch =3D False + self.inrange =3D True + self.invert =3D False + self.eraser =3D False + elif state =3D=3D PenState.PEN_IS_IN_CONTACT: + self.tipswitch =3D True + self.inrange =3D True + self.invert =3D False + self.eraser =3D False + elif state =3D=3D PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT: + self.tipswitch =3D False + self.inrange =3D True + self.invert =3D True + self.eraser =3D False + elif state =3D=3D PenState.PEN_IS_ERASING: + self.tipswitch =3D False + self.inrange =3D True + self.invert =3D True + self.eraser =3D True + + self.current_state =3D state + + def __assert_axis(self, evdev, axis, value): + if ( + axis =3D=3D libevdev.EV_KEY.BTN_TOOL_RUBBER + and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None + ): + return + + assert ( + evdev.value[axis] =3D=3D value + ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) !=3D {value}" + + def assert_expected_input_events(self, evdev): + assert evdev.value[libevdev.EV_ABS.ABS_X] =3D=3D self.x + assert evdev.value[libevdev.EV_ABS.ABS_Y] =3D=3D self.y + assert self.current_state =3D=3D PenState.from_evdev(evdev) + + class PenDigitizer(base.UHIDTestDevice): def __init__( self, @@ -486,7 +486,7 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=3D["scribb= le", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=3Dk) for k, v in Pen.legal_transitions().i= tems()], + [pytest.param(v, id=3Dk) for k, v in PenState.legal_transition= s().items()], ) def test_valid_pen_states(self, state_list, scribble): """This is the first half of the Windows Pen Implementation st= ate machine: @@ -498,7 +498,7 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=3D["scribb= le", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=3Dk) for k, v in Pen.tolerated_transitions= ().items()], + [pytest.param(v, id=3Dk) for k, v in PenState.tolerated_transi= tions().items()], ) def test_tolerated_pen_states(self, state_list, scribble): """This is not adhering to the Windows Pen Implementation stat= e machine @@ -515,7 +515,7 @@ class BaseTest: "state_list", [ pytest.param(v, id=3Dk) - for k, v in Pen.legal_transitions_with_invert().items() + for k, v in PenState.legal_transitions_with_invert().items= () ], ) def test_valid_invert_pen_states(self, state_list, scribble): @@ -535,7 +535,7 @@ class BaseTest: "state_list", [ pytest.param(v, id=3Dk) - for k, v in Pen.tolerated_transitions_with_invert().items() + for k, v in PenState.tolerated_transitions_with_invert().i= tems() ], ) def test_tolerated_invert_pen_states(self, state_list, scribble): @@ -553,7 +553,7 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=3D["scribb= le", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=3Dk) for k, v in Pen.broken_transitions().= items()], + [pytest.param(v, id=3Dk) for k, v in PenState.broken_transitio= ns().items()], ) def test_tolerated_broken_pen_states(self, state_list, scribble): """Those tests are definitely not part of the Windows specific= ation. --=20 2.41.0