#!/usr/bin/python3 # -*- coding: utf-8 -*- # key-mapper - GUI for device specific keyboard mappings # Copyright (C) 2021 sezanzeb # # This file is part of key-mapper. # # key-mapper 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. # # key-mapper 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 key-mapper. If not, see . import os import unittest import json import evdev from evdev.ecodes import EV_KEY, KEY_A from keymapper.paths import CONFIG_PATH from keymapper.groups import ( _FindGroups, groups, classify, GAMEPAD, MOUSE, UNKNOWN, GRAPHICS_TABLET, TOUCHPAD, KEYBOARD, _Group, ) from tests.test import quick_cleanup, fixtures class FakePipe: groups = None def send(self, groups): self.groups = groups class TestGroups(unittest.TestCase): def tearDown(self): quick_cleanup() def test_group(self): group = _Group( paths=["/dev/a", "/dev/b", "/dev/c"], names=["name_bar", "name_a", "name_foo"], types=[MOUSE, KEYBOARD, UNKNOWN], key="key", ) self.assertEqual(group.name, "name_a") self.assertEqual(group.key, "key") self.assertEqual( group.get_preset_path("preset1234"), os.path.join(CONFIG_PATH, "presets", group.name, "preset1234.json"), ) def test_find_groups(self): pipe = FakePipe() _FindGroups(pipe).run() self.assertIsInstance(pipe.groups, str) groups.loads(pipe.groups) self.maxDiff = None self.assertEqual( groups.dumps(), json.dumps( [ json.dumps( { "paths": [ "/dev/input/event1", ], "names": ["Foo Device"], "types": [KEYBOARD], "key": "Foo Device", } ), json.dumps( { "paths": [ "/dev/input/event11", "/dev/input/event10", "/dev/input/event13", ], "names": ["Foo Device foo", "Foo Device", "Foo Device"], "types": [KEYBOARD, MOUSE], "key": "Foo Device 2", } ), json.dumps( { "paths": ["/dev/input/event20"], "names": ["Bar Device"], "types": [KEYBOARD], "key": "Bar Device", } ), json.dumps( { "paths": ["/dev/input/event30"], "names": ["gamepad"], "types": [GAMEPAD], "key": "gamepad", } ), json.dumps( { "paths": ["/dev/input/event40"], "names": ["key-mapper Bar Device"], "types": [KEYBOARD], "key": "key-mapper Bar Device", } ), ] ), ) groups2 = json.dumps( [group.dumps() for group in groups.filter(include_keymapper=True)] ) self.assertEqual(pipe.groups, groups2) def test_list_group_names(self): self.assertListEqual( groups.list_group_names(), [ "Foo Device", "Foo Device", "Bar Device", "gamepad", ], ) def test_filter(self): # by default no key-mapper devices are present filtered = groups.filter() keys = [group.key for group in filtered] self.assertIn("Foo Device 2", keys) self.assertNotIn("key-mapper Bar Device", keys) def test_skip_camera(self): fixtures["/foo/bar"] = { "name": "camera", "phys": "abcd1", "info": evdev.DeviceInfo(1, 2, 3, 4), "capabilities": {evdev.ecodes.EV_KEY: [evdev.ecodes.KEY_CAMERA]}, } groups.refresh() self.assertIsNone(groups.find(name="camera")) self.assertIsNotNone(groups.find(name="gamepad")) def test_device_with_only_ev_abs(self): # could be anything, a lot of devices have ABS_X capabilities, # so it is not treated as gamepad joystick and since it also # doesn't have key capabilities, there is nothing to map. fixtures["/foo/bar"] = { "name": "qux", "phys": "abcd2", "info": evdev.DeviceInfo(1, 2, 3, 4), "capabilities": {evdev.ecodes.EV_ABS: [evdev.ecodes.ABS_X]}, } groups.refresh() self.assertIsNotNone(groups.find(name="gamepad")) self.assertIsNone(groups.find(name="qux")) # verify this test even works at all fixtures["/foo/bar"]["capabilities"][EV_KEY] = [KEY_A] groups.refresh() self.assertIsNotNone(groups.find(name="qux")) def test_duplicate_device(self): fixtures["/dev/input/event20"]["name"] = "Foo Device" groups.refresh() group1 = groups.find(key="Foo Device") group2 = groups.find(key="Foo Device 2") group3 = groups.find(key="Foo Device 3") self.assertIn("/dev/input/event1", group1.paths) self.assertIn("/dev/input/event10", group2.paths) self.assertIn("/dev/input/event20", group3.paths) self.assertEqual(group1.key, "Foo Device") self.assertEqual(group2.key, "Foo Device 2") self.assertEqual(group3.key, "Foo Device 3") self.assertEqual(group1.name, "Foo Device") self.assertEqual(group2.name, "Foo Device") self.assertEqual(group3.name, "Foo Device") def test_classify(self): # properly detects if the device is a gamepad EV_ABS = evdev.ecodes.EV_ABS EV_KEY = evdev.ecodes.EV_KEY EV_REL = evdev.ecodes.EV_REL class FakeDevice: def __init__(self, capabilities): self.c = capabilities def capabilities(self, absinfo): assert not absinfo return self.c """gamepads""" self.assertEqual( classify( FakeDevice( { EV_ABS: [evdev.ecodes.ABS_X, evdev.ecodes.ABS_Y], EV_KEY: [evdev.ecodes.BTN_A], } ) ), GAMEPAD, ) """mice""" self.assertEqual( classify( FakeDevice( { EV_REL: [ evdev.ecodes.REL_X, evdev.ecodes.REL_Y, evdev.ecodes.REL_WHEEL, ], EV_KEY: [evdev.ecodes.BTN_LEFT], } ) ), MOUSE, ) """keyboard""" self.assertEqual(classify(FakeDevice({EV_KEY: [evdev.ecodes.KEY_A]})), KEYBOARD) """touchpads""" self.assertEqual( classify( FakeDevice( { EV_KEY: [evdev.ecodes.KEY_A], EV_ABS: [evdev.ecodes.ABS_MT_POSITION_X], } ) ), TOUCHPAD, ) """graphics tablets""" self.assertEqual( classify( FakeDevice( { EV_ABS: [evdev.ecodes.ABS_X, evdev.ecodes.ABS_Y], EV_KEY: [evdev.ecodes.BTN_STYLUS], } ) ), GRAPHICS_TABLET, ) """weird combos""" self.assertEqual( classify( FakeDevice( { EV_ABS: [evdev.ecodes.ABS_X, evdev.ecodes.ABS_Y], EV_KEY: [evdev.ecodes.KEY_1], } ) ), UNKNOWN, ) self.assertEqual( classify( FakeDevice({EV_ABS: [evdev.ecodes.ABS_X], EV_KEY: [evdev.ecodes.BTN_A]}) ), UNKNOWN, ) self.assertEqual(classify(FakeDevice({EV_KEY: [evdev.ecodes.BTN_A]})), UNKNOWN) self.assertEqual(classify(FakeDevice({EV_ABS: [evdev.ecodes.ABS_X]})), UNKNOWN) if __name__ == "__main__": unittest.main()