diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py index cd9c1269afa6..ddf28c245046 100644 --- a/tools/testing/selftests/hid/tests/test_tablet.py +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -132,6 +132,136 @@ class PenState(Enum): return tuple() + @staticmethod + 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 + """ + return { + "in-range": (PenState.PEN_IS_IN_RANGE,), + "in-range -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT), + "in-range -> touch -> release": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> touch -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + 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 with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,), + "hover-erasing -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> erase": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + ), + "hover-erasing -> erase -> release": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erasing -> erase -> release -> out-of-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + "hover-erasing -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_RANGE, + ), + "in-range -> hover-erasing": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + } + + @staticmethod + def tolerated_transitions() -> Dict[str, Tuple["PenState", ...]]: + """This is not adhering to the Windows Pen Implementation state machine + but we should expect the kernel to behave properly, mostly for historical + reasons.""" + return { + "direct-in-contact": (PenState.PEN_IS_IN_CONTACT,), + "direct-in-contact -> out-of-range": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + 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 with the intend + to erase. + https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states + """ + return { + "direct-erase": (PenState.PEN_IS_ERASING,), + "direct-erase -> out-of-range": ( + PenState.PEN_IS_ERASING, + PenState.PEN_IS_OUT_OF_RANGE, + ), + } + + @staticmethod + def broken_transitions() -> Dict[str, Tuple["PenState", ...]]: + """Those tests are definitely not part of the Windows specification. + However, a half broken device might export those transitions. + For example, a pen that has the eraser button might wobble between + touching and erasing if the tablet doesn't enforce the Windows + state machine.""" + return { + "in-range -> touch -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "in-range -> erase -> hover-erase": ( + PenState.PEN_IS_IN_RANGE, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + ), + "hover-erase -> erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "hover-erase -> touch -> in-range": ( + PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_IN_RANGE, + ), + "touch -> erase -> touch -> erase": ( + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + PenState.PEN_IS_IN_CONTACT, + PenState.PEN_IS_ERASING, + ), + } + class Pen(object): def __init__(self, x, y): @@ -228,136 +358,6 @@ class Pen(object): assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y assert self.current_state == PenState.from_evdev(evdev) - @staticmethod - 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 - """ - return { - "in-range": (PenState.PEN_IS_IN_RANGE,), - "in-range -> out-of-range": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_OUT_OF_RANGE, - ), - "in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT), - "in-range -> touch -> release": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_IN_RANGE, - ), - "in-range -> touch -> release -> out-of-range": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_OUT_OF_RANGE, - ), - } - - @staticmethod - 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 with the intend - to erase. - https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states - """ - return { - "hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,), - "hover-erasing -> out-of-range": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_OUT_OF_RANGE, - ), - "hover-erasing -> erase": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_ERASING, - ), - "hover-erasing -> erase -> release": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - ), - "hover-erasing -> erase -> release -> out-of-range": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_OUT_OF_RANGE, - ), - "hover-erasing -> in-range": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_IN_RANGE, - ), - "in-range -> hover-erasing": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - ), - } - - @staticmethod - def tolerated_transitions() -> Dict[str, Tuple[PenState, ...]]: - """This is not adhering to the Windows Pen Implementation state machine - but we should expect the kernel to behave properly, mostly for historical - reasons.""" - return { - "direct-in-contact": (PenState.PEN_IS_IN_CONTACT,), - "direct-in-contact -> out-of-range": ( - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_OUT_OF_RANGE, - ), - } - - @staticmethod - 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 with the intend - to erase. - https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states - """ - return { - "direct-erase": (PenState.PEN_IS_ERASING,), - "direct-erase -> out-of-range": ( - PenState.PEN_IS_ERASING, - PenState.PEN_IS_OUT_OF_RANGE, - ), - } - - @staticmethod - def broken_transitions() -> Dict[str, Tuple[PenState, ...]]: - """Those tests are definitely not part of the Windows specification. - However, a half broken device might export those transitions. - For example, a pen that has the eraser button might wobble between - touching and erasing if the tablet doesn't enforce the Windows - state machine.""" - return { - "in-range -> touch -> erase -> hover-erase": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - ), - "in-range -> erase -> hover-erase": ( - PenState.PEN_IS_IN_RANGE, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - ), - "hover-erase -> erase -> touch -> in-range": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_IN_RANGE, - ), - "hover-erase -> touch -> in-range": ( - PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_IN_RANGE, - ), - "touch -> erase -> touch -> erase": ( - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_ERASING, - PenState.PEN_IS_IN_CONTACT, - PenState.PEN_IS_ERASING, - ), - } - class PenDigitizer(base.UHIDTestDevice): def __init__( @@ -486,7 +486,7 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=k) for k, v in Pen.legal_transitions().items()], + [pytest.param(v, id=k) for k, v in PenState.legal_transitions().items()], ) def test_valid_pen_states(self, state_list, scribble): """This is the first half of the Windows Pen Implementation state machine: @@ -498,7 +498,10 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=k) for k, v in Pen.tolerated_transitions().items()], + [ + pytest.param(v, id=k) + for k, v in PenState.tolerated_transitions().items() + ], ) def test_tolerated_pen_states(self, state_list, scribble): """This is not adhering to the Windows Pen Implementation state machine @@ -515,7 +518,7 @@ class BaseTest: "state_list", [ pytest.param(v, id=k) - 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 +538,7 @@ class BaseTest: "state_list", [ pytest.param(v, id=k) - for k, v in Pen.tolerated_transitions_with_invert().items() + for k, v in PenState.tolerated_transitions_with_invert().items() ], ) def test_tolerated_invert_pen_states(self, state_list, scribble): @@ -553,7 +556,7 @@ class BaseTest: @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"]) @pytest.mark.parametrize( "state_list", - [pytest.param(v, id=k) for k, v in Pen.broken_transitions().items()], + [pytest.param(v, id=k) for k, v in PenState.broken_transitions().items()], ) def test_tolerated_broken_pen_states(self, state_list, scribble): """Those tests are definitely not part of the Windows specification.