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:
246
tests/unit/test_macros/test_mouse.py
Normal file
246
tests/unit/test_macros/test_mouse.py
Normal file
@ -0,0 +1,246 @@
|
||||
#!/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 (
|
||||
REL_Y,
|
||||
EV_REL,
|
||||
REL_HWHEEL,
|
||||
REL_HWHEEL_HI_RES,
|
||||
REL_X,
|
||||
REL_WHEEL,
|
||||
REL_WHEEL_HI_RES,
|
||||
KEY_A,
|
||||
EV_KEY,
|
||||
)
|
||||
|
||||
from inputremapper.configs.validation_errors import MacroError
|
||||
from inputremapper.injection.macros.parse import Parser
|
||||
from tests.lib.test_setup import test_setup
|
||||
from tests.unit.test_macros.macro_test_base import DummyMapping, MacroTestBase
|
||||
|
||||
|
||||
@test_setup
|
||||
class TestMouse(MacroTestBase):
|
||||
async def test_mouse_acceleration(self):
|
||||
# There is a tiny float-rounding error that can break the test, therefore I use
|
||||
# 0.09001 to make it more robust.
|
||||
await self._run_macro("mouse(up, 10, 0.09001)", 0.1)
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_Y, -2),
|
||||
(EV_REL, REL_Y, -3),
|
||||
(EV_REL, REL_Y, -4),
|
||||
(EV_REL, REL_Y, -4),
|
||||
(EV_REL, REL_Y, -5),
|
||||
],
|
||||
self.result,
|
||||
)
|
||||
|
||||
async def test_rate(self):
|
||||
# It should move 200 times per second by 1px, for 0.2 seconds.
|
||||
rel_rate = 200
|
||||
time = 0.2
|
||||
speed = 1
|
||||
expected_movement = time * rel_rate * speed
|
||||
|
||||
await self._run_macro(f"mouse(down, {speed})", time, rel_rate)
|
||||
total_movement = sum(event[2] for event in self.result)
|
||||
self.assertAlmostEqual(float(total_movement), expected_movement, delta=1)
|
||||
|
||||
async def test_slow_movement(self):
|
||||
await self._run_macro("mouse(down, 0.1)", 0.2, 200)
|
||||
total_movement = sum(event[2] for event in self.result)
|
||||
self.assertAlmostEqual(total_movement, 4, delta=1)
|
||||
|
||||
async def test_mouse_xy_acceleration_1(self):
|
||||
await self._run_macro("mouse_xy(2, -10, 0.09001)", 0.1)
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_Y, -2),
|
||||
(EV_REL, REL_Y, -3),
|
||||
(EV_REL, REL_Y, -4),
|
||||
(EV_REL, REL_Y, -4),
|
||||
(EV_REL, REL_Y, -5),
|
||||
],
|
||||
self._get_y_movement(),
|
||||
)
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_X, 1),
|
||||
(EV_REL, REL_X, 1),
|
||||
(EV_REL, REL_X, 1),
|
||||
],
|
||||
self._get_x_movement(),
|
||||
)
|
||||
|
||||
async def test_mouse_xy_acceleration_2(self):
|
||||
await self._run_macro("mouse_xy(10, -2, 0.09001)", 0.1)
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_Y, -1),
|
||||
(EV_REL, REL_Y, -1),
|
||||
(EV_REL, REL_Y, -1),
|
||||
],
|
||||
self._get_y_movement(),
|
||||
)
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_X, 2),
|
||||
(EV_REL, REL_X, 3),
|
||||
(EV_REL, REL_X, 4),
|
||||
(EV_REL, REL_X, 4),
|
||||
(EV_REL, REL_X, 5),
|
||||
],
|
||||
self._get_x_movement(),
|
||||
)
|
||||
|
||||
async def test_mouse_xy_only_x(self):
|
||||
await self._run_macro("mouse_xy(x=10, acceleration=1)", 0.1)
|
||||
self.assertEqual([], self._get_y_movement())
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_X, 10),
|
||||
(EV_REL, REL_X, 10),
|
||||
(EV_REL, REL_X, 10),
|
||||
(EV_REL, REL_X, 10),
|
||||
(EV_REL, REL_X, 10),
|
||||
(EV_REL, REL_X, 10),
|
||||
],
|
||||
self._get_x_movement(),
|
||||
)
|
||||
|
||||
async def test_mouse_xy_only_y(self):
|
||||
await self._run_macro("mouse_xy(y=10)", 0.1)
|
||||
self.assertEqual([], self._get_x_movement())
|
||||
self.assertEqual(
|
||||
[
|
||||
(EV_REL, REL_Y, 10),
|
||||
(EV_REL, REL_Y, 10),
|
||||
(EV_REL, REL_Y, 10),
|
||||
(EV_REL, REL_Y, 10),
|
||||
(EV_REL, REL_Y, 10),
|
||||
(EV_REL, REL_Y, 10),
|
||||
],
|
||||
self._get_y_movement(),
|
||||
)
|
||||
|
||||
async def test_wheel_left(self):
|
||||
wheel_speed = 60
|
||||
sleep = 0.1
|
||||
await self._run_macro(f"wheel(left, {wheel_speed})", sleep)
|
||||
|
||||
self.assertIn((EV_REL, REL_HWHEEL, 1), self.result)
|
||||
self.assertIn((EV_REL, REL_HWHEEL_HI_RES, 60), self.result)
|
||||
|
||||
expected_num_hires_events = sleep * DummyMapping.rel_rate
|
||||
expected_num_wheel_events = int(expected_num_hires_events / 120 * wheel_speed)
|
||||
actual_num_wheel_events = self.result.count((EV_REL, REL_HWHEEL, 1))
|
||||
actual_num_hires_events = self.result.count(
|
||||
(
|
||||
EV_REL,
|
||||
REL_HWHEEL_HI_RES,
|
||||
wheel_speed,
|
||||
)
|
||||
)
|
||||
|
||||
self.assertGreater(
|
||||
actual_num_wheel_events,
|
||||
expected_num_wheel_events * 0.9,
|
||||
)
|
||||
self.assertLess(
|
||||
actual_num_wheel_events,
|
||||
expected_num_wheel_events * 1.1,
|
||||
)
|
||||
self.assertGreater(
|
||||
actual_num_hires_events,
|
||||
expected_num_hires_events * 0.9,
|
||||
)
|
||||
self.assertLess(
|
||||
actual_num_hires_events,
|
||||
expected_num_hires_events * 1.1,
|
||||
)
|
||||
|
||||
async def test_wheel_up(self):
|
||||
await self._run_macro("wheel(up, 60)", 0.1)
|
||||
self.assertIn((EV_REL, REL_WHEEL, 1), self.result)
|
||||
self.assertIn((EV_REL, REL_WHEEL_HI_RES, 60), self.result)
|
||||
|
||||
async def test_wheel_down(self):
|
||||
await self._run_macro("wheel(down, 60)", 0.1)
|
||||
self.assertIn((EV_REL, REL_WHEEL, -1), self.result)
|
||||
self.assertIn((EV_REL, REL_WHEEL_HI_RES, -60), self.result)
|
||||
|
||||
async def test_wheel_right(self):
|
||||
await self._run_macro("wheel(right, 60)", 0.1)
|
||||
self.assertIn((EV_REL, REL_HWHEEL, -1), self.result)
|
||||
self.assertIn((EV_REL, REL_HWHEEL_HI_RES, -60), self.result)
|
||||
|
||||
async def test_mouse_releases(self):
|
||||
await self._run_macro("mouse(down, 1).key(a)", 0.1)
|
||||
self.assertEqual(self.result[-2:], [(EV_KEY, KEY_A, 1), (EV_KEY, KEY_A, 0)])
|
||||
|
||||
async def test_mouse_xy_releases(self):
|
||||
await self._run_macro("mouse_xy(1, 1, 1).key(a)", 0.1)
|
||||
self.assertEqual(self.result[-2:], [(EV_KEY, KEY_A, 1), (EV_KEY, KEY_A, 0)])
|
||||
|
||||
async def test_wheel_releases(self):
|
||||
await self._run_macro("wheel(down, 1).key(a)", 0.1)
|
||||
self.assertEqual(self.result[-2:], [(EV_KEY, KEY_A, 1), (EV_KEY, KEY_A, 0)])
|
||||
|
||||
async def test_raises_error(self):
|
||||
Parser.parse("mouse(up, 3)", self.context) # no error
|
||||
Parser.parse("mouse(up, speed=$a)", self.context) # no error
|
||||
self.assertRaises(MacroError, Parser.parse, "mouse(3, up)", self.context)
|
||||
Parser.parse("wheel(left, 3)", self.context) # no error
|
||||
self.assertRaises(MacroError, Parser.parse, "wheel(3, left)", self.context)
|
||||
|
||||
def _get_x_movement(self):
|
||||
return [event for event in self.result if event[1] == REL_X]
|
||||
|
||||
def _get_y_movement(self):
|
||||
return [event for event in self.result if event[1] == REL_Y]
|
||||
|
||||
async def _run_macro(
|
||||
self,
|
||||
code: str,
|
||||
time: float,
|
||||
rel_rate: int = DummyMapping.rel_rate,
|
||||
):
|
||||
dummy_mapping = DummyMapping()
|
||||
dummy_mapping.rel_rate = rel_rate
|
||||
macro = Parser.parse(
|
||||
code,
|
||||
self.context,
|
||||
dummy_mapping,
|
||||
)
|
||||
macro.press_trigger()
|
||||
asyncio.ensure_future(macro.run(self.handler))
|
||||
|
||||
await asyncio.sleep(time)
|
||||
self.assertTrue(macro.tasks[0].is_holding())
|
||||
macro.release_trigger()
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user