proper test util imports, switched to the recommended pydbus package

This commit is contained in:
sezanzeb 2020-12-02 14:36:17 +01:00 committed by sezanzeb
parent 8a9b815772
commit 39905db774
13 changed files with 96 additions and 87 deletions

View File

@ -1,5 +1,5 @@
[run] [run]
branch = True branch = True
source = keymapper source = /usr/lib/python3.8/site-packages/keymapper
concurrency = multiprocessing concurrency = multiprocessing
debug = multiproc debug = multiproc

View File

@ -2,5 +2,5 @@ Package: key-mapper
Version: 0.1.0 Version: 0.1.0
Architecture: all Architecture: all
Maintainer: Sezanzeb <proxima@hip70890b.de> Maintainer: Sezanzeb <proxima@hip70890b.de>
Depends: build-essential, libpython3-dev, libdbus-1-dev, python3, python3-setuptools, python3-evdev, python3-dbus, python3-gi Depends: build-essential, libpython3-dev, libdbus-1-dev, python3, python3-setuptools, python3-evdev, python3-pydbus, python3-gi
Description: A tool to change the mapping of your input device buttons Description: A tool to change the mapping of your input device buttons

View File

@ -59,8 +59,7 @@ groups
##### Git/pip ##### Git/pip
```bash ```bash
git clone https://github.com/sezanzeb/key-mapper.git sudo pip install git+https://github.com/sezanzeb/key-mapper.git
cd key-mapper && sudo python3 setup.py install
``` ```
##### Manjaro/Arch ##### Manjaro/Arch
@ -100,6 +99,6 @@ sudo dpkg -i python3-key-mapper_0.1.0-1_all.deb
```bash ```bash
pylint keymapper --extension-pkg-whitelist=evdev pylint keymapper --extension-pkg-whitelist=evdev
sudo pip install -e . && coverage run tests/test.py sudo pip install . && coverage run tests/test.py
coverage combine && coverage report -m coverage combine && coverage report -m
``` ```

View File

@ -29,12 +29,11 @@ from argparse import ArgumentParser
import gi import gi
gi.require_version('GLib', '2.0') gi.require_version('GLib', '2.0')
from gi.repository import GLib from gi.repository import GLib
import dbus.mainloop.glib from pydbus import SessionBus
from dbus.mainloop.glib import DBusGMainLoop
from keymapper.logger import logger, update_verbosity, log_info, \ from keymapper.logger import update_verbosity, log_info, \
add_filehandler add_filehandler
from keymapper.daemon import Daemon from keymapper.daemon import Daemon, BUS_NAME
from keymapper.dev.permissions import can_read_devices from keymapper.dev.permissions import can_read_devices
@ -60,12 +59,11 @@ if __name__ == '__main__':
can_read_devices() can_read_devices()
session_bus = dbus.SessionBus(mainloop=DBusGMainLoop()) bus = SessionBus()
name = dbus.service.BusName('keymapper.Control', session_bus) loop = GLib.MainLoop()
daemon = Daemon(session_bus, '/') daemon = Daemon(True, loop)
bus.publish(BUS_NAME, daemon)
atexit.register(daemon.stop) atexit.register(lambda: daemon.stop(True))
logger.info('Daemon running, waiting for dbus messages') loop.run()
mainloop = GLib.MainLoop()
mainloop.run()

View File

@ -19,16 +19,15 @@
# along with key-mapper. If not, see <https://www.gnu.org/licenses/>. # along with key-mapper. If not, see <https://www.gnu.org/licenses/>.
"""Starts injecting keycodes based on the configuration.""" """Starts injecting keycodes based on the configuration.
https://github.com/LEW21/pydbus/tree/cc407c8b1d25b7e28a6d661a29f9e661b1c9b964/examples/clientserver # noqa pylint: disable=line-too-long
"""
import subprocess import subprocess
# TODO https://www.freedesktop.org/wiki/Software/DBusBindings/#python from pydbus import SessionBus
# says "New applications should use pydbus"
import dbus
from dbus import service
import dbus.mainloop.glib
from keymapper.logger import logger from keymapper.logger import logger
from keymapper.dev.injector import KeycodeInjector from keymapper.dev.injector import KeycodeInjector
@ -36,6 +35,9 @@ from keymapper.mapping import Mapping
from keymapper.config import config from keymapper.config import config
BUS_NAME = 'keymapper.Control'
def is_service_running(): def is_service_running():
"""Check if the daemon is running.""" """Check if the daemon is running."""
try: try:
@ -54,23 +56,13 @@ def get_dbus_interface():
) )
return Daemon(autoload=False) return Daemon(autoload=False)
try: bus = SessionBus()
bus = dbus.SessionBus() interface = bus.get(BUS_NAME)
remote_object = bus.get_object('keymapper.Control', '/')
interface = dbus.Interface(remote_object, 'keymapper.Interface')
logger.debug('Connected to dbus')
except dbus.exceptions.DBusException as error:
logger.warning(
'Could not connect to the dbus of "key-mapper-service", mapping '
'keys only works as long as the window is open.'
)
logger.debug(error)
return Daemon(autoload=False)
return interface return interface
class Daemon(service.Object): class Daemon:
"""Starts injecting keycodes based on the configuration. """Starts injecting keycodes based on the configuration.
Can be talked to either over dbus or by instantiating it. Can be talked to either over dbus or by instantiating it.
@ -80,9 +72,34 @@ class Daemon(service.Object):
continue to do so afterwards, but it can't decide to start injecting continue to do so afterwards, but it can't decide to start injecting
on its own. on its own.
""" """
def __init__(self, *args, autoload=True, **kwargs):
dbus = f"""
<node>
<interface name='{BUS_NAME}'>
<method name='stop_injecting'>
<arg type='s' name='device' direction='in'/>
</method>
<method name='start_injecting'>
<arg type='s' name='device' direction='in'/>
<arg type='s' name='preset' direction='in'/>
<arg type='b' name='response' direction='out'/>
</method>
<method name='stop'>
<arg type='b' name='terminate' direction='in'/>
</method>
<method name='hello'>
<arg type='s' name='out' direction='in'/>
<arg type='s' name='response' direction='out'/>
</method>
</interface>
</node>
"""
def __init__(self, autoload=True, loop=None):
"""Constructs the daemon. You still need to run the GLib mainloop.""" """Constructs the daemon. You still need to run the GLib mainloop."""
logger.debug('Creating daemon')
self.injectors = {} self.injectors = {}
self.loop = loop
if autoload: if autoload:
for device, preset in config.iterate_autoload_presets(): for device, preset in config.iterate_autoload_presets():
mapping = Mapping() mapping = Mapping()
@ -93,9 +110,7 @@ class Daemon(service.Object):
self.injectors[device] = injector self.injectors[device] = injector
except OSError as error: except OSError as error:
logger.error(error) logger.error(error)
super().__init__(*args, **kwargs)
@dbus.service.method('keymapper.Interface', in_signature='s')
def stop_injecting(self, device): def stop_injecting(self, device):
"""Stop injecting the mapping for a single device.""" """Stop injecting the mapping for a single device."""
if self.injectors.get(device) is None: if self.injectors.get(device) is None:
@ -107,9 +122,6 @@ class Daemon(service.Object):
self.injectors[device].stop_injecting() self.injectors[device].stop_injecting()
# TODO if ss is the correct signature for multiple parameters, add an
# example to https://gitlab.freedesktop.org/dbus/dbus-python/-/blob/master/doc/tutorial.txt # noqa pylint: disable=line-too-long
@dbus.service.method('keymapper.Interface', in_signature='ss')
def start_injecting(self, device, preset): def start_injecting(self, device, preset):
"""Start injecting the preset for the device. """Start injecting the preset for the device.
@ -138,7 +150,6 @@ class Daemon(service.Object):
return True return True
@dbus.service.method('keymapper.Interface', in_signature='b')
def stop(self, terminate=False): def stop(self, terminate=False):
"""Stop all injections and end the service. """Stop all injections and end the service.
@ -147,5 +158,10 @@ class Daemon(service.Object):
for injector in self.injectors.values(): for injector in self.injectors.values():
injector.stop_injecting() injector.stop_injecting()
if terminate: if terminate and self.loop:
exit(0) logger.debug('Daemon stops')
self.loop.quit()
def hello(self, out):
"""Used for tests."""
return out

View File

@ -45,6 +45,6 @@ setup(
install_requires=[ install_requires=[
'setuptools', 'setuptools',
'evdev', 'evdev',
'dbus-python' 'pydbus'
] ]
) )

View File

@ -37,7 +37,21 @@ from keymapper.logger import update_verbosity
assert not os.getcwd().endswith('tests') assert not os.getcwd().endswith('tests')
sys.path = [os.path.abspath('.')] + sys.path
def is_service_running():
"""Check if the daemon is running."""
try:
subprocess.check_output(['pgrep', '-f', 'key-mapper-service'])
except subprocess.CalledProcessError:
return
# let tests control daemon existance
raise Exception('Expected the service not to be running already.')
is_service_running()
# make sure the "tests" module visible
sys.path.append(os.getcwd())
# give tests some time to test stuff while the process # give tests some time to test stuff while the process
# is still running # is still running
@ -178,12 +192,6 @@ def patch_evdev():
def list_devices(): def list_devices():
return fixtures.keys() return fixtures.keys()
"""
rlist = {device.fd: device for device in self.virtual_devices}
while True:
ready = select.select(rlist, [], [])[0]
"""
class InputDevice: class InputDevice:
# expose as existing attribute, otherwise the patch for # expose as existing attribute, otherwise the patch for
# evdev < 1.0.0 will crash the test # evdev < 1.0.0 will crash the test
@ -281,19 +289,6 @@ def clear_write_history():
while uinput_write_history_pipe[0].poll(): while uinput_write_history_pipe[0].poll():
uinput_write_history_pipe[0].recv() uinput_write_history_pipe[0].recv()
def is_service_running():
"""Check if the daemon is running."""
try:
subprocess.check_output(['pgrep', '-f', 'key-mapper-service'])
except subprocess.CalledProcessError:
return
# let tests control daemon existance
raise Exception('Expected the service not to be running already.')
is_service_running()
# quickly fake some stuff before any other file gets a chance to import # quickly fake some stuff before any other file gets a chance to import
# the original versions # the original versions
patch_paths() patch_paths()
@ -321,15 +316,17 @@ def main():
'testcases', pattern='*.py' 'testcases', pattern='*.py'
) )
# add a newline to each "qux (foo.bar)..." output, because the logs # add a newline to each "qux (foo.bar)..." output before each test,
# will be on the same line otherwise # because the first log will be on the same line otherwise
originalStartTest = unittest.TextTestResult.startTest original_start_test = unittest.TextTestResult.startTest
def startTest(self, test):
originalStartTest(self, test)
print()
unittest.TextTestResult.startTest = startTest
testrunner = unittest.TextTestRunner(verbosity=2).run(testsuite) def start_test(self, test):
original_start_test(self, test)
print()
unittest.TextTestResult.startTest = start_test
unittest.TextTestRunner(verbosity=2).run(testsuite)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -24,7 +24,6 @@ import multiprocessing
import unittest import unittest
import time import time
import dbus
import evdev import evdev
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
@ -33,9 +32,9 @@ from gi.repository import Gtk
from keymapper.state import custom_mapping, system_mapping, \ from keymapper.state import custom_mapping, system_mapping, \
clear_system_mapping clear_system_mapping
from keymapper.config import config from keymapper.config import config
from keymapper.daemon import Daemon, get_dbus_interface from keymapper.daemon import Daemon, get_dbus_interface, BUS_NAME
from test import uinput_write_history_pipe, Event, pending_events from tests.test import uinput_write_history_pipe, Event, pending_events
def gtk_iteration(): def gtk_iteration():
@ -49,7 +48,7 @@ class TestDBusDaemon(unittest.TestCase):
def setUpClass(cls): def setUpClass(cls):
cls.process = multiprocessing.Process( cls.process = multiprocessing.Process(
target=os.system, target=os.system,
args=('key-mapper-service',) args=('key-mapper-service -d',)
) )
cls.process.start() cls.process.start()
time.sleep(0.5) time.sleep(0.5)
@ -57,13 +56,13 @@ class TestDBusDaemon(unittest.TestCase):
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
try: cls.interface.stop(True)
cls.interface.stop(True)
except dbus.exceptions.DBusException:
pass
def test_can_connect(self): def test_can_connect(self):
self.assertIsInstance(self.interface, dbus.Interface) # it's a remote dbus object
self.assertEqual(self.interface._bus_name, BUS_NAME)
self.assertFalse(isinstance(self.interface, Daemon))
self.assertEqual(self.interface.hello('foo'), 'foo')
class TestDaemon(unittest.TestCase): class TestDaemon(unittest.TestCase):

View File

@ -32,7 +32,7 @@ from keymapper.state import custom_mapping, system_mapping, \
from keymapper.mapping import Mapping from keymapper.mapping import Mapping
from keymapper.config import config from keymapper.config import config
from test import uinput_write_history, Event, pending_events, fixtures, \ from tests.test import uinput_write_history, Event, pending_events, fixtures, \
clear_write_history, EVENT_READ_TIMEOUT, uinput_write_history_pipe, \ clear_write_history, EVENT_READ_TIMEOUT, uinput_write_history_pipe, \
MAX_ABS MAX_ABS

View File

@ -40,7 +40,7 @@ from keymapper.paths import CONFIG, get_config_path
from keymapper.config import config from keymapper.config import config
from keymapper.dev.reader import keycode_reader from keymapper.dev.reader import keycode_reader
from test import tmp, pending_events, Event, uinput_write_history_pipe, \ from tests.test import tmp, pending_events, Event, uinput_write_history_pipe, \
clear_write_history clear_write_history

View File

@ -27,7 +27,7 @@ import logging
from keymapper.logger import logger, add_filehandler, update_verbosity, \ from keymapper.logger import logger, add_filehandler, update_verbosity, \
log_info log_info
from test import tmp from tests.test import tmp
class TestLogger(unittest.TestCase): class TestLogger(unittest.TestCase):

View File

@ -29,7 +29,7 @@ from keymapper.presets import find_newest_preset, rename_preset, \
from keymapper.paths import CONFIG from keymapper.paths import CONFIG
from keymapper.state import custom_mapping from keymapper.state import custom_mapping
from test import tmp from tests.test import tmp
def create_preset(device, name='new preset'): def create_preset(device, name='new preset'):

View File

@ -26,7 +26,7 @@ import time
from keymapper.dev.reader import keycode_reader from keymapper.dev.reader import keycode_reader
from test import Event, pending_events, EVENT_READ_TIMEOUT from tests.test import Event, pending_events, EVENT_READ_TIMEOUT
CODE_1 = 100 CODE_1 = 100