input-remapper/tests/unit/test_input_config.py

576 lines
20 KiB
Python
Raw Normal View History

2022-12-15 13:43:03 +00:00
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# input-remapper - GUI for device specific keyboard mappings
2023-02-27 16:07:42 +00:00
# Copyright (C) 2023 sezanzeb <proxima@sezanzeb.de>
2022-12-15 13:43:03 +00:00
#
# 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 unittest
from evdev.ecodes import (
EV_KEY,
EV_ABS,
EV_REL,
BTN_C,
BTN_B,
BTN_A,
BTN_MIDDLE,
REL_X,
REL_Y,
REL_WHEEL,
REL_HWHEEL,
ABS_RY,
ABS_X,
ABS_HAT0Y,
ABS_HAT0X,
KEY_A,
KEY_LEFTSHIFT,
KEY_RIGHTALT,
KEY_LEFTCTRL,
)
from inputremapper.configs.input_config import InputCombination, InputConfig
class TestInputConfig(unittest.TestCase):
def test_input_config(self):
test_cases = [
# basic test, nothing fancy here
{
"input": {
"type": EV_KEY,
"code": KEY_A,
"origin_hash": "foo",
},
"properties": {
"type": EV_KEY,
"code": KEY_A,
"origin_hash": "foo",
"input_match_hash": (EV_KEY, KEY_A, "foo"),
"defines_analog_input": False,
"type_and_code": (EV_KEY, KEY_A),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "a",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_KEY, KEY_A, "foo", None)),
},
],
},
# removes analog_threshold
{
"input": {
"type": EV_KEY,
"code": KEY_A,
"origin_hash": "foo",
"analog_threshold": 10,
},
"properties": {
"type": EV_KEY,
"code": KEY_A,
"origin_hash": "foo",
"analog_threshold": None,
"input_match_hash": (EV_KEY, KEY_A, "foo"),
"defines_analog_input": False,
"type_and_code": (EV_KEY, KEY_A),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "a",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_KEY, KEY_A, "foo", None)),
},
],
},
# abs to btn
{
"input": {
"type": EV_ABS,
"code": ABS_X,
"origin_hash": "foo",
"analog_threshold": 10,
},
"properties": {
"type": EV_ABS,
"code": ABS_X,
"origin_hash": "foo",
"analog_threshold": 10,
"input_match_hash": (EV_ABS, ABS_X, "foo"),
"defines_analog_input": False,
"type_and_code": (EV_ABS, ABS_X),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "Joystick-X Right 10%",
},
{
"name": "description",
"args": (),
"kwargs": {"exclude_threshold": True},
"return": "Joystick-X Right",
},
{
"name": "description",
"args": (),
"kwargs": {
"exclude_threshold": True,
"exclude_direction": True,
},
"return": "Joystick-X",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_ABS, ABS_X, "foo", 10)),
},
],
},
# abs to btn with d-pad
{
"input": {
"type": EV_ABS,
"code": ABS_HAT0Y,
"origin_hash": "foo",
"analog_threshold": 10,
},
"properties": {
"type": EV_ABS,
"code": ABS_HAT0Y,
"origin_hash": "foo",
"analog_threshold": 10,
"input_match_hash": (EV_ABS, ABS_HAT0Y, "foo"),
"defines_analog_input": False,
"type_and_code": (EV_ABS, ABS_HAT0Y),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "DPad-Y Down 10%",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_ABS, ABS_HAT0Y, "foo", 10)),
},
],
},
# rel to btn
{
"input": {
"type": EV_REL,
"code": REL_Y,
"origin_hash": "foo",
"analog_threshold": 10,
},
"properties": {
"type": EV_REL,
"code": REL_Y,
"origin_hash": "foo",
"analog_threshold": 10,
"input_match_hash": (EV_REL, REL_Y, "foo"),
"defines_analog_input": False,
"type_and_code": (EV_REL, REL_Y),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "Y Down 10",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_REL, REL_Y, "foo", 10)),
},
],
},
# abs as axis
{
"input": {
"type": EV_ABS,
"code": ABS_X,
"origin_hash": "foo",
"analog_threshold": 0,
},
"properties": {
"type": EV_ABS,
"code": ABS_X,
"origin_hash": "foo",
"analog_threshold": None,
"input_match_hash": (EV_ABS, ABS_X, "foo"),
"defines_analog_input": True,
"type_and_code": (EV_ABS, ABS_X),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "Joystick-X",
},
{
"name": "description",
"args": (),
"kwargs": {
"exclude_threshold": True,
"exclude_direction": True,
},
"return": "Joystick-X",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_ABS, ABS_X, "foo", None)),
},
],
},
# rel as axis
{
"input": {
"type": EV_REL,
"code": REL_WHEEL,
"origin_hash": "foo",
},
"properties": {
"type": EV_REL,
"code": REL_WHEEL,
"origin_hash": "foo",
"analog_threshold": None,
"input_match_hash": (EV_REL, REL_WHEEL, "foo"),
"defines_analog_input": True,
"type_and_code": (EV_REL, REL_WHEEL),
},
"methods": [
{
"name": "description",
"args": (),
"kwargs": {},
"return": "Wheel",
},
{
"name": "__hash__",
"args": (),
"kwargs": {},
"return": hash((EV_REL, REL_WHEEL, "foo", None)),
},
],
},
]
for test_case in test_cases:
input_config = InputConfig(**test_case["input"])
for property_, value in test_case["properties"].items():
self.assertEqual(
value,
getattr(input_config, property_),
f"property mismatch for input: {test_case['input']} "
f"property: {property_} expected value: {value}",
)
for method in test_case["methods"]:
self.assertEqual(
method["return"],
getattr(input_config, method["name"])(
*method["args"], **method["kwargs"]
),
f"wrong method return for input: {test_case['input']} "
f"method: {method}",
)
def test_is_immutable(self):
input_config = InputConfig(type=1, code=2)
with self.assertRaises(TypeError):
input_config.origin_hash = "foo"
class TestInputCombination(unittest.TestCase):
def test_eq(self):
a = InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="1234"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="abcd"),
]
)
b = InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="1234"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="abcd"),
]
)
self.assertEqual(a, b)
def test_not_eq(self):
a = InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="2345"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="bcde"),
]
)
b = InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="1234"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="abcd"),
]
)
self.assertNotEqual(a, b)
def test_can_be_used_as_dict_key(self):
dict_ = {
InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="1234"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="abcd"),
]
): "foo"
}
key = InputCombination(
[
InputConfig(type=EV_REL, code=REL_X, value=1, origin_hash="1234"),
InputConfig(type=EV_KEY, code=KEY_A, value=1, origin_hash="abcd"),
]
)
self.assertEqual(dict_.get(key), "foo")
2022-12-15 13:43:03 +00:00
def test_get_permutations(self):
key_1 = InputCombination(InputCombination.from_tuples((1, 3, 1)))
2022-12-15 13:43:03 +00:00
self.assertEqual(len(key_1.get_permutations()), 1)
self.assertEqual(key_1.get_permutations()[0], key_1)
key_2 = InputCombination(InputCombination.from_tuples((1, 3, 1), (1, 5, 1)))
2022-12-15 13:43:03 +00:00
self.assertEqual(len(key_2.get_permutations()), 1)
self.assertEqual(key_2.get_permutations()[0], key_2)
key_3 = InputCombination(
InputCombination.from_tuples((1, 3, 1), (1, 5, 1), (1, 7, 1))
2022-12-15 13:43:03 +00:00
)
self.assertEqual(len(key_3.get_permutations()), 2)
self.assertEqual(
key_3.get_permutations()[0],
InputCombination(
InputCombination.from_tuples((1, 3, 1), (1, 5, 1), (1, 7, 1))
),
2022-12-15 13:43:03 +00:00
)
self.assertEqual(
key_3.get_permutations()[1],
InputCombination(
InputCombination.from_tuples((1, 5, 1), (1, 3, 1), (1, 7, 1))
),
2022-12-15 13:43:03 +00:00
)
def test_is_problematic(self):
key_1 = InputCombination(
InputCombination.from_tuples((1, KEY_LEFTSHIFT, 1), (1, 5, 1))
2022-12-15 13:43:03 +00:00
)
self.assertTrue(key_1.is_problematic())
key_2 = InputCombination(
InputCombination.from_tuples((1, KEY_RIGHTALT, 1), (1, 5, 1))
2022-12-15 13:43:03 +00:00
)
self.assertTrue(key_2.is_problematic())
key_3 = InputCombination(
InputCombination.from_tuples((1, 3, 1), (1, KEY_LEFTCTRL, 1))
2022-12-15 13:43:03 +00:00
)
self.assertTrue(key_3.is_problematic())
key_4 = InputCombination(InputCombination.from_tuples((1, 3, 1)))
2022-12-15 13:43:03 +00:00
self.assertFalse(key_4.is_problematic())
key_5 = InputCombination(InputCombination.from_tuples((1, 3, 1), (1, 5, 1)))
2022-12-15 13:43:03 +00:00
self.assertFalse(key_5.is_problematic())
def test_init(self):
self.assertRaises(TypeError, lambda: InputCombination(1))
self.assertRaises(TypeError, lambda: InputCombination(None))
self.assertRaises(TypeError, lambda: InputCombination([1]))
self.assertRaises(TypeError, lambda: InputCombination((1,)))
self.assertRaises(TypeError, lambda: InputCombination((1, 2)))
self.assertRaises(TypeError, lambda: InputCombination("1"))
self.assertRaises(TypeError, lambda: InputCombination("(1,2,3)"))
self.assertRaises(
TypeError,
lambda: InputCombination(((1, 2, 3), (1, 2, 3), None)),
)
# those don't raise errors
InputCombination(({"type": 1, "code": 2}, {"type": 1, "code": 1}))
InputCombination(({"type": 1, "code": 2},))
InputCombination(({"type": "1", "code": "2"},))
InputCombination([InputConfig(type=1, code=2, analog_threshold=3)])
2022-12-15 13:43:03 +00:00
InputCombination(
(
{"type": 1, "code": 2},
{"type": "1", "code": "2"},
InputConfig(type=1, code=2),
)
)
def test_to_config(self):
c1 = InputCombination([InputConfig(type=1, code=2, analog_threshold=3)])
2022-12-15 13:43:03 +00:00
c2 = InputCombination(
(
InputConfig(type=1, code=2, analog_threshold=3),
InputConfig(type=4, code=5, analog_threshold=6),
)
)
# analog_threshold is removed for key events
self.assertEqual(c1.to_config(), ({"type": 1, "code": 2},))
self.assertEqual(
c2.to_config(),
({"type": 1, "code": 2}, {"type": 4, "code": 5, "analog_threshold": 6}),
)
def test_beautify(self):
# not an integration test, but I have all the selection_label tests here already
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_KEY, KEY_A, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"a",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_KEY, KEY_A, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"a",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_ABS, ABS_HAT0Y, -1))
2022-12-15 13:43:03 +00:00
).beautify(),
"DPad-Y Up",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_KEY, BTN_A, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"Button A",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_KEY, 1234, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"unknown (1, 1234)",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_ABS, ABS_HAT0X, -1))
2022-12-15 13:43:03 +00:00
).beautify(),
"DPad-X Left",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_ABS, ABS_HAT0Y, -1))
2022-12-15 13:43:03 +00:00
).beautify(),
"DPad-Y Up",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_KEY, BTN_A, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"Button A",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_ABS, ABS_X, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"Joystick-X Right",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_ABS, ABS_RY, 1))
).beautify(),
2022-12-15 13:43:03 +00:00
"Joystick-RY Down",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_REL, REL_HWHEEL, 1))
2022-12-15 13:43:03 +00:00
).beautify(),
"Wheel Right",
)
self.assertEqual(
InputCombination(
InputCombination.from_tuples((EV_REL, REL_WHEEL, -1))
2022-12-15 13:43:03 +00:00
).beautify(),
"Wheel Down",
)
# combinations
self.assertEqual(
InputCombination(
InputCombination.from_tuples(
2022-12-15 13:43:03 +00:00
(EV_KEY, BTN_A, 1),
(EV_KEY, BTN_B, 1),
(EV_KEY, BTN_C, 1),
),
).beautify(),
"Button A + Button B + Button C",
)
def test_find_analog_input_config(self):
analog_input = InputConfig(type=EV_REL, code=REL_X)
combination = InputCombination(
(
InputConfig(type=EV_KEY, code=BTN_MIDDLE),
InputConfig(type=EV_REL, code=REL_Y, analog_threshold=1),
analog_input,
)
)
self.assertIsNone(combination.find_analog_input_config(type_=EV_ABS))
self.assertEqual(
combination.find_analog_input_config(type_=EV_REL), analog_input
)
self.assertEqual(combination.find_analog_input_config(), analog_input)
combination = InputCombination(
(
InputConfig(type=EV_REL, code=REL_X, analog_threshold=1),
InputConfig(type=EV_KEY, code=BTN_MIDDLE),
)
)
self.assertIsNone(combination.find_analog_input_config(type_=EV_ABS))
self.assertIsNone(combination.find_analog_input_config(type_=EV_REL))
self.assertIsNone(combination.find_analog_input_config())
if __name__ == "__main__":
unittest.main()