mirror of
https://github.com/torvalds/linux.git
synced 2026-05-02 05:22:49 -04:00
selftests/hid: tablets: move the transitions to PenState
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. Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net> Acked-by: Jiri Kosina <jkosina@suse.com> Link: https://lore.kernel.org/r/20231206-wip-selftests-v2-5-c0350c2f5986@kernel.org Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user