summaryrefslogtreecommitdiff
path: root/utils/trackertestutils
diff options
context:
space:
mode:
authorSam Thursfield <sam@afuera.me.uk>2020-05-21 12:42:24 +0200
committerSam Thursfield <sam@afuera.me.uk>2020-06-13 15:38:35 +0200
commit032e7baeffc02d9d8f6e285e11f5fb2973ba59a2 (patch)
treeef115650baadb37fd2dc151b87cec73b35439605 /utils/trackertestutils
parent6d407e8aad3fba2f869cd7ad922dc8e79a27d96e (diff)
downloadtracker-032e7baeffc02d9d8f6e285e11f5fb2973ba59a2.tar.gz
trackertestutils: Add an optional D-Bus system bus to the sandbox
This allows us to work with umockdev to simulate hardware events.
Diffstat (limited to 'utils/trackertestutils')
-rw-r--r--utils/trackertestutils/__main__.py2
-rw-r--r--utils/trackertestutils/dbusdaemon.py64
-rw-r--r--utils/trackertestutils/helpers.py2
-rw-r--r--utils/trackertestutils/sandbox.py58
4 files changed, 99 insertions, 27 deletions
diff --git a/utils/trackertestutils/__main__.py b/utils/trackertestutils/__main__.py
index ba64eb4d7..991da79bf 100644
--- a/utils/trackertestutils/__main__.py
+++ b/utils/trackertestutils/__main__.py
@@ -297,7 +297,7 @@ class MinerStatusWatch():
def setup(self):
log.debug(f"Set up status watch on {self.dbus_name}")
self._proxy = Gio.DBusProxy.new_sync(
- self._sandbox.get_connection(),
+ self._sandbox.get_session_bus_connection(),
Gio.DBusProxyFlags.NONE, None,
self.dbus_name, self.object_path, 'org.freedesktop.Tracker3.Miner',
None)
diff --git a/utils/trackertestutils/dbusdaemon.py b/utils/trackertestutils/dbusdaemon.py
index e55c03595..67c947af5 100644
--- a/utils/trackertestutils/dbusdaemon.py
+++ b/utils/trackertestutils/dbusdaemon.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2018,2019, Sam Thursfield <sam@afuera.me.uk>
+# Copyright (C) 2018-2020, Sam Thursfield <sam@afuera.me.uk>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -26,19 +26,49 @@ import signal
import subprocess
import threading
+from . import mainloop
+
+DEFAULT_TIMEOUT = 10
+
log = logging.getLogger(__name__)
-dbus_stderr_log = logging.getLogger(__name__ + '.stderr')
-dbus_stdout_log = logging.getLogger(__name__ + '.stdout')
class DaemonNotStartedError(Exception):
pass
+def await_bus_name(conn, bus_name, timeout=DEFAULT_TIMEOUT):
+ """Blocks until 'bus_name' has an owner."""
+
+ log.info("Blocking until name %s has owner", bus_name)
+ loop = mainloop.MainLoop()
+
+ def name_appeared_cb(connection, name, name_owner):
+ log.info("Name %s appeared (owned by %s)", name, name_owner)
+ loop.quit()
+
+ def timeout_cb():
+ log.info("Timeout fired after %s seconds", timeout)
+ raise AwaitTimeoutException(
+ f"Timeout awaiting bus name '{bus_name}'")
+
+ watch_id = Gio.bus_watch_name_on_connection(
+ conn, bus_name, Gio.BusNameWatcherFlags.NONE, name_appeared_cb, None)
+ timeout_id = GLib.timeout_add_seconds(timeout, timeout_cb)
+
+ loop.run_checked()
+
+ Gio.bus_unwatch_name(watch_id)
+ GLib.source_remove(timeout_id)
+
+
class DBusDaemon:
- """The private D-Bus instance that provides the sandbox's session bus."""
+ """A private D-Bus daemon instance."""
+
+ def __init__(self, config_file=None, name='dbus-daemon'):
+ self.name = name
+ self.config_file = config_file
- def __init__(self):
self.process = None
self.address = None
@@ -77,12 +107,13 @@ class DBusDaemon:
return dbus_daemon
- def start(self, config_file=None, env=None, new_session=False):
+ def start(self, env=None, new_session=False):
dbus_command = [self._dbus_daemon_path(), '--print-address=1', '--print-pid=1']
- if config_file:
- dbus_command += ['--config-file=' + config_file]
+ if self.config_file:
+ dbus_command += ['--config-file=' + self.config_file]
else:
dbus_command += ['--session']
+
log.debug("Running: %s", dbus_command)
self.process = subprocess.Popen(
dbus_command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
@@ -101,10 +132,13 @@ class DBusDaemon:
log.debug("Using new D-Bus session with address '%s' with PID %d",
self.address, self.pid)
+ stderr_log = logging.getLogger(self.name + '.stderr')
+ stdout_log = logging.getLogger(self.name + '.stdout')
+
# We must read from the pipes continuously, otherwise the daemon
# process will block.
- self._threads=[threading.Thread(target=self.pipe_to_log, args=(self.process.stdout, dbus_stdout_log), daemon=True),
- threading.Thread(target=self.pipe_to_log, args=(self.process.stderr, dbus_stdout_log), daemon=True)]
+ self._threads=[threading.Thread(target=self.pipe_to_log, args=(self.process.stdout, stdout_log), daemon=True),
+ threading.Thread(target=self.pipe_to_log, args=(self.process.stderr, stderr_log), daemon=True)]
self._threads[0].start()
self._threads[1].start()
@@ -199,3 +233,13 @@ class DBusDaemon:
return None
else:
raise
+
+ def activate_service(self, bus_name, object_path):
+ GDBUS_DEFAULT_TIMEOUT = -1
+ self.get_connection().call_sync(
+ bus_name, object_path, 'org.freedesktop.DBus.Peer', 'Ping',
+ None, None, Gio.DBusCallFlags.NONE, GDBUS_DEFAULT_TIMEOUT, None)
+ self.await_bus_name(bus_name)
+
+ def await_bus_name(self, bus_name, timeout=DEFAULT_TIMEOUT):
+ await_bus_name(self.get_connection(), bus_name, timeout)
diff --git a/utils/trackertestutils/helpers.py b/utils/trackertestutils/helpers.py
index c1795b974..91086618f 100644
--- a/utils/trackertestutils/helpers.py
+++ b/utils/trackertestutils/helpers.py
@@ -17,5 +17,5 @@
# FIXME: Compatibility module due to recent API breaks.
# Remove this before 3.0.
-from .sandbox import TrackerSandbox
+from .sandbox import TrackerSandbox as TrackerDBusSandbox
from .storehelper import StoreHelper
diff --git a/utils/trackertestutils/sandbox.py b/utils/trackertestutils/sandbox.py
index ce2b51866..55897b184 100644
--- a/utils/trackertestutils/sandbox.py
+++ b/utils/trackertestutils/sandbox.py
@@ -23,10 +23,13 @@ Sandbox environment for running tests.
The sandbox is essentially a private D-Bus daemon.
"""
+from gi.repository import Gio
+
import atexit
import logging
import os
import signal
+import subprocess
from . import dbusdaemon
from . import dconf
@@ -51,16 +54,25 @@ atexit.register(_cleanup_processes)
class TrackerSandbox:
"""
- Private D-Bus session bus which executes a sandboxed Tracker instance.
+ Run Tracker daemons isolated from the real user session.
+
+ The primary method of sandboxing is running one or more private D-Bus
+ daemons, which take place of the host's session and system bus.
"""
- def __init__(self, dbus_daemon_config_file, extra_env=None):
- self.dbus_daemon_config_file = dbus_daemon_config_file
+ def __init__(self, session_bus_config_file, system_bus_config_file=None,
+ extra_env=None):
self.extra_env = extra_env or {}
- self.daemon = dbusdaemon.DBusDaemon()
+ self.session_bus = dbusdaemon.DBusDaemon(
+ name='sandbox-session-bus', config_file=session_bus_config_file)
+ if system_bus_config_file:
+ self.system_bus = dbusdaemon.DBusDaemon(
+ name='sandbox-system-bus', config_file=system_bus_config_file)
+ else:
+ self.system_bus = None
- def start(self, new_session=False):
+ def get_environment(self):
env = os.environ
env.update(self.extra_env)
env['G_MESSAGES_PREFIXED'] = 'all'
@@ -81,17 +93,25 @@ class TrackerSandbox:
if xdg_runtime_dir:
os.makedirs(xdg_runtime_dir, exist_ok=True)
- log.info("Starting D-Bus daemon for sandbox.")
+ def start(self, new_session=False):
+ if self.system_bus:
+ log.info("Starting D-Bus system bus for sandbox.")
+ log.debug("Added environment variables: %s", self.extra_env)
+ self.system_bus.start(env=self.get_environment(), new_session=new_session)
+
+ self.extra_env['DBUS_SYSTEM_BUS_ADDRESS'] = self.system_bus.get_address()
+
+ log.info("Starting D-Bus session bus for sandbox.")
log.debug("Added environment variables: %s", self.extra_env)
- self.daemon.start(self.dbus_daemon_config_file, env=env, new_session=new_session)
+ self.session_bus.start(env=self.get_environment(), new_session=new_session)
def stop(self):
tracker_processes = []
- log.info("Looking for active Tracker processes on the bus")
- for busname in self.daemon.list_names_sync():
+ log.info("Looking for active Tracker processes on the session bus")
+ for busname in self.session_bus.list_names_sync():
if busname.startswith(TRACKER_DBUS_PREFIX):
- pid = self.daemon.get_connection_unix_process_id_sync(busname)
+ pid = self.session_bus.get_connection_unix_process_id_sync(busname)
if pid is not None:
tracker_processes.append(pid)
@@ -109,8 +129,12 @@ class TrackerSandbox:
#
# (tracker-miner-fs:14955): GLib-GIO-CRITICAL **: 11:38:40.386: Error while sending AddMatch() message: The connection is closed
- log.info("Stopping D-Bus daemon for sandbox.")
- self.daemon.stop()
+ log.info("Stopping D-Bus session bus for sandbox.")
+ self.session_bus.stop()
+
+ if self.system_bus:
+ log.info("Stopping D-Bus system bus for sandbox.")
+ self.system_bus.stop()
def stop_daemon(self, busname):
"""Stops the daemon that owns 'busname'.
@@ -128,12 +152,16 @@ class TrackerSandbox:
else:
log.info("Couldn't find a process owning %s.", busname)
- def get_connection(self):
- return self.daemon.get_connection()
+ def get_session_bus_connection(self):
+ """Return a Gio.BusConnection to the sandbox D-Bus session bus."""
+ return self.session_bus.get_connection()
+ def get_system_bus_connection(self):
+ """Return a Gio.BusConnection to the sandbox D-Bus system bus."""
+ return self.system_bus.get_connection()
def get_session_bus_address(self):
- return self.daemon.get_address()
+ return self.session_bus.get_address()
def set_config(self, schema_config_dict):
"""Set config values in multiple GSettings schemas.