chore(repo): baseline du fork input-remapper (upstream v2.2.1)
Le dépôt ne contenait que README.md ; ajout de l'intégralité du code fonctionnel du fork comme socle stable de la branche de release. L'état runtime d'orchestration (.ideai/) est exclu du suivi. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
180
tests/unit/test_event_pipeline/test_rel_to_btn.py
Normal file
180
tests/unit/test_event_pipeline/test_rel_to_btn.py
Normal file
@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2025 sezanzeb <b8x45ygc9@mozmail.com>
|
||||
#
|
||||
# This file is part of input-remapper.
|
||||
#
|
||||
# input-remapper 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# input-remapper 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 input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
import unittest
|
||||
|
||||
from evdev.ecodes import (
|
||||
EV_KEY,
|
||||
EV_REL,
|
||||
REL_X,
|
||||
REL_HWHEEL,
|
||||
REL_WHEEL,
|
||||
)
|
||||
|
||||
from inputremapper.configs.mapping import (
|
||||
Mapping,
|
||||
)
|
||||
from inputremapper.configs.preset import Preset
|
||||
from inputremapper.configs.keyboard_layout import keyboard_layout
|
||||
from inputremapper.configs.input_config import InputCombination, InputConfig
|
||||
from inputremapper.input_event import InputEvent
|
||||
from tests.lib.fixtures import fixtures
|
||||
from tests.lib.test_setup import test_setup
|
||||
from tests.unit.test_event_pipeline.event_pipeline_test_base import (
|
||||
EventPipelineTestBase,
|
||||
)
|
||||
|
||||
|
||||
@test_setup
|
||||
class TestRelToBtn(EventPipelineTestBase):
|
||||
async def test_rel_to_btn(self):
|
||||
"""Rel axis mapped to buttons are automatically released if no new rel event arrives."""
|
||||
|
||||
# map those two to stuff
|
||||
w_up = (EV_REL, REL_WHEEL, -1)
|
||||
hw_right = (EV_REL, REL_HWHEEL, 1)
|
||||
|
||||
# should be forwarded and present in the capabilities
|
||||
hw_left = (EV_REL, REL_HWHEEL, -1)
|
||||
|
||||
keyboard_layout.clear()
|
||||
code_b = 91
|
||||
code_c = 92
|
||||
keyboard_layout._set("b", code_b)
|
||||
keyboard_layout._set("c", code_c)
|
||||
|
||||
# set a high release timeout to make sure the tests pass
|
||||
release_timeout = 0.2
|
||||
mapping_1 = Mapping.from_combination(
|
||||
InputCombination(InputCombination.from_tuples(hw_right)), "keyboard", "k(b)"
|
||||
)
|
||||
mapping_2 = Mapping.from_combination(
|
||||
InputCombination(InputCombination.from_tuples(w_up)), "keyboard", "c"
|
||||
)
|
||||
mapping_1.release_timeout = release_timeout
|
||||
mapping_2.release_timeout = release_timeout
|
||||
|
||||
preset = Preset()
|
||||
preset.add(mapping_1)
|
||||
preset.add(mapping_2)
|
||||
|
||||
event_reader = self.create_event_reader(preset, fixtures.foo_device_2_mouse)
|
||||
|
||||
await self.send_events(
|
||||
[InputEvent.from_tuple(hw_right), InputEvent.from_tuple(w_up)] * 5,
|
||||
event_reader,
|
||||
)
|
||||
# wait less than the release timeout and send more events
|
||||
await asyncio.sleep(release_timeout / 5)
|
||||
await self.send_events(
|
||||
[InputEvent.from_tuple(hw_right), InputEvent.from_tuple(w_up)] * 5
|
||||
+ [InputEvent.from_tuple(hw_left)]
|
||||
* 3, # one event will release hw_right, the others are forwarded
|
||||
event_reader,
|
||||
)
|
||||
# wait more than the release_timeout to make sure all handlers finish
|
||||
await asyncio.sleep(release_timeout * 1.2)
|
||||
|
||||
keyboard_history = self.global_uinputs.get_uinput("keyboard").write_history
|
||||
forwarded_history = self.forward_uinput.write_history
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, code_b, 1)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, code_c, 1)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, code_b, 0)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, code_c, 0)), 1)
|
||||
# the unmapped wheel direction
|
||||
self.assertEqual(forwarded_history.count(hw_left), 2)
|
||||
|
||||
# the unmapped wheel won't get a debounced release command, it's
|
||||
# forwarded as is
|
||||
self.assertNotIn((EV_REL, REL_HWHEEL, 0), forwarded_history)
|
||||
|
||||
async def test_rel_trigger_threshold(self):
|
||||
"""Test that different activation points for rel_to_btn work correctly."""
|
||||
|
||||
# at 5 map to a
|
||||
mapping_1 = Mapping.from_combination(
|
||||
InputCombination(
|
||||
[InputConfig(type=EV_REL, code=REL_X, analog_threshold=5)]
|
||||
),
|
||||
output_symbol="a",
|
||||
)
|
||||
# at 15 map to b
|
||||
mapping_2 = Mapping.from_combination(
|
||||
InputCombination(
|
||||
[InputConfig(type=EV_REL, code=REL_X, analog_threshold=15)]
|
||||
),
|
||||
output_symbol="b",
|
||||
)
|
||||
release_timeout = 0.2 # give some time to do assertions before the release
|
||||
mapping_1.release_timeout = release_timeout
|
||||
mapping_2.release_timeout = release_timeout
|
||||
preset = Preset()
|
||||
preset.add(mapping_1)
|
||||
preset.add(mapping_2)
|
||||
|
||||
a = keyboard_layout.get("a")
|
||||
b = keyboard_layout.get("b")
|
||||
|
||||
event_reader = self.create_event_reader(preset, fixtures.foo_device_2_mouse)
|
||||
|
||||
await self.send_events(
|
||||
[
|
||||
InputEvent.rel(REL_X, -5), # forward
|
||||
InputEvent.rel(REL_X, 0), # forward
|
||||
InputEvent.rel(REL_X, 3), # forward
|
||||
InputEvent.rel(REL_X, 10), # trigger a
|
||||
],
|
||||
event_reader,
|
||||
)
|
||||
await asyncio.sleep(release_timeout * 1.5) # release a
|
||||
keyboard_history = self.global_uinputs.get_uinput("keyboard").write_history
|
||||
|
||||
self.assertEqual(keyboard_history, [(EV_KEY, a, 1), (EV_KEY, a, 0)])
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, a, 1)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, a, 0)), 1)
|
||||
self.assertNotIn((EV_KEY, b, 1), keyboard_history)
|
||||
|
||||
await self.send_events(
|
||||
[
|
||||
InputEvent.rel(REL_X, 10), # trigger a
|
||||
InputEvent.rel(REL_X, 20), # trigger b
|
||||
InputEvent.rel(REL_X, 10), # release b
|
||||
],
|
||||
event_reader,
|
||||
)
|
||||
keyboard_history = self.global_uinputs.get_uinput("keyboard").write_history
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, a, 1)), 2)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, b, 1)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, b, 0)), 1)
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, a, 0)), 1)
|
||||
|
||||
await asyncio.sleep(release_timeout * 1.5) # release a
|
||||
keyboard_history = self.global_uinputs.get_uinput("keyboard").write_history
|
||||
forwarded_history = self.forward_uinput.write_history
|
||||
self.assertEqual(keyboard_history.count((EV_KEY, a, 0)), 2)
|
||||
self.assertEqual(
|
||||
forwarded_history,
|
||||
[(EV_REL, REL_X, -5), (EV_REL, REL_X, 0), (EV_REL, REL_X, 3)],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user