[tracker/sam/functional-tests-quiet: 3/15] WIP: functional-tests: Start daemons through D-Bus autolaunch



commit ef5417e046428dc6e37fec75a3f1bd2dbb5b500b
Author: Sam Thursfield <sam afuera me uk>
Date:   Thu Aug 29 16:28:01 2019 +0300

    WIP: functional-tests: Start daemons through D-Bus autolaunch
    
    Instead of manually running and managing Tracker daemon processes
    manually in the test, we now rely on our private D-Bus daemon to
    do so.
    
    This makes the test environment more like a real Tracker deployment.
    
    Log output from the D-Bus daemon is now captured and output through
    the Python logging system. This allows for finer-grained filtering
    of output from the tests themselves and from the Tracker daemons.

 meson.build                                  |  13 --
 tests/functional-tests/configuration.json.in |  10 +-
 tests/functional-tests/configuration.py      |  20 ++-
 tests/functional-tests/meson.build           |  37 ++---
 tests/functional-tests/storetest.py          |  16 +-
 utils/trackertestutils/dbusdaemon.py         |  69 +++++++--
 utils/trackertestutils/helpers.py            | 216 +++++----------------------
 7 files changed, 147 insertions(+), 234 deletions(-)
---
diff --git a/meson.build b/meson.build
index 24582c820..a445cde1c 100644
--- a/meson.build
+++ b/meson.build
@@ -285,19 +285,6 @@ conf.set('tracker_store', join_paths ('${libexecdir}', 'tracker-store'))
 conf.set('ontologies_dir', join_paths ('${datadir}', 'tracker', 'ontologies'))
 conf.set('domain_ontologies_dir', join_paths('${datadir}', 'tracker', 'domain-ontologies'))
 
-# Configure functional tests to run completely from source tree.
-conf.set('FUNCTIONAL_TESTS_ONTOLOGIES_DIR', join_paths(meson.current_source_dir(), 'tests', 
'functional-tests', 'test-ontologies'))
-conf.set('FUNCTIONAL_TESTS_TRACKER_STORE_PATH', join_paths(meson.current_build_dir(), 'src', 
'tracker-store', 'tracker-store'))
-
-# This is set in an awkward way for compatibility with Autoconf. Switch it
-# to a normal boolean once we get rid of the Autotools build system. It's
-# only used in tests/functional-tests/common/utils/configuration.py.in.
-if get_option('journal')
-    conf.set('DISABLE_JOURNAL_TRUE', 'true')
-else
-    conf.set('DISABLE_JOURNAL_TRUE', '')
-endif
-
 configure_file(input: 'config.h.meson.in',
                output: 'config.h',
                configuration: conf)
diff --git a/tests/functional-tests/configuration.json.in b/tests/functional-tests/configuration.json.in
index 46c5126f7..ee23be36f 100644
--- a/tests/functional-tests/configuration.json.in
+++ b/tests/functional-tests/configuration.json.in
@@ -1,5 +1,9 @@
 {
-    "TEST_ONTOLOGIES_DIR": "@FUNCTIONAL_TESTS_ONTOLOGIES_DIR@",
-    "TRACKER_STORE_PATH": "@FUNCTIONAL_TESTS_TRACKER_STORE_PATH@",
-    "disableJournal": "@DISABLE_JOURNAL_TRUE@"
+    "TEST_DBUS_DAEMON_CONFIG_FILE": "@TEST_DBUS_DAEMON_CONFIG_FILE@",
+    "TEST_DCONF_PROFILE": "@TEST_DCONF_PROFILE@",
+    "TEST_GSETTINGS_SCHEMA_DIR": "@TEST_GSETTINGS_SCHEMA_DIR@",
+    "TEST_LANGUAGE_STOP_WORDS_DIR": "@TEST_LANGUAGE_STOP_WORDS_DIR@",
+    "TEST_ONTOLOGIES_DIR": "@TEST_ONTOLOGIES_DIR@",
+    "TEST_DOMAIN_ONTOLOGY_RULE": "@TEST_DOMAIN_ONTOLOGY_RULE@",
+    "disableJournal": "@DISABLE_JOURNAL@"
 }
diff --git a/tests/functional-tests/configuration.py b/tests/functional-tests/configuration.py
index 938ad0f19..7bc96bec4 100644
--- a/tests/functional-tests/configuration.py
+++ b/tests/functional-tests/configuration.py
@@ -18,10 +18,10 @@
 # 02110-1301, USA.
 #
 
-
 import json
 import logging
 import os
+import pathlib
 import sys
 
 
@@ -34,13 +34,19 @@ with open(os.environ['TRACKER_FUNCTIONAL_TEST_CONFIG']) as f:
     config = json.load(f)
 
 
-TOP_SRCDIR = os.path.dirname(os.path.dirname(
-    os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
-TOP_BUILDDIR = os.environ['TRACKER_FUNCTIONAL_TEST_BUILD_DIR']
+TEST_DBUS_DAEMON_CONFIG_FILE = config['TEST_DBUS_DAEMON_CONFIG_FILE']
+TEST_ONTOLOGIES_DIR = str(pathlib.Path(__file__).parent.joinpath('test-ontologies'))
+disableJournal = bool(config['disableJournal'])
+
 
-TEST_ONTOLOGIES_DIR = config['TEST_ONTOLOGIES_DIR']
-TRACKER_STORE_PATH = config['TRACKER_STORE_PATH']
-disableJournal = (len(config['disableJournal']) == 0)
+def test_environment():
+    return {
+        'DCONF_PROFILE': config['TEST_DCONF_PROFILE'],
+        'GSETTINGS_SCHEMA_DIR': config['TEST_GSETTINGS_SCHEMA_DIR'],
+        'TRACKER_DB_ONTOLOGIES_DIR': config['TEST_ONTOLOGIES_DIR'],
+        'TRACKER_LANGUAGE_STOP_WORDS_DIR': config['TEST_LANGUAGE_STOP_WORDS_DIR'],
+        'TRACKER_TEST_DOMAIN_ONTOLOGY_RULE': config['TEST_DOMAIN_ONTOLOGY_RULE'],
+    }
 
 
 def get_environment_boolean(variable):
diff --git a/tests/functional-tests/meson.build b/tests/functional-tests/meson.build
index be3fc2a4a..6671e18c1 100644
--- a/tests/functional-tests/meson.build
+++ b/tests/functional-tests/meson.build
@@ -1,13 +1,22 @@
-test_runner = configure_file(
-    input: 'test-runner.sh.in',
-    output: 'test-runner.sh',
-    configuration: conf)
-test_runner = find_program(test_runner)
+# Configure functional tests to run completely from source tree.
+testconf = configuration_data()
+
+config_json_full_path = join_paths(meson.current_build_dir(), 'configuration.json')
+dconf_profile_full_path = join_paths(meson.current_source_dir(), 'trackertest')
+
+testconf.set10('DISABLE_JOURNAL', not get_option('journal'))
+testconf.set('TEST_ONTOLOGIES_DIR', join_paths(meson.current_source_dir(), 'tests', 'functional-tests', 
'test-ontologies'))
+testconf.set('TEST_DBUS_DAEMON_CONFIG_FILE', join_paths(build_root, 'tests', 'test-bus.conf'))
+testconf.set('TEST_DCONF_PROFILE', dconf_profile_full_path)
+testconf.set('TEST_DOMAIN_ONTOLOGY_RULE', tracker_uninstalled_domain_rule)
+testconf.set('TEST_GSETTINGS_SCHEMA_DIR', tracker_uninstalled_gsettings_schema_dir)
+testconf.set('TEST_ONTOLOGIES_DIR', tracker_uninstalled_nepomuk_ontologies_dir)
+testconf.set('TEST_LANGUAGE_STOP_WORDS_DIR', tracker_uninstalled_stop_words_dir)
 
 config_json = configure_file(
   input: 'configuration.json.in',
   output: 'configuration.json',
-  configuration: conf
+  configuration: testconf
 )
 
 functional_tests = [
@@ -26,28 +35,20 @@ functional_tests = [
   '17-ontology-changes',
 ]
 
-config_json_full_path = join_paths(meson.current_build_dir(), 'configuration.json')
-dconf_profile_full_path = join_paths(meson.current_source_dir(), 'trackertest')
-
 test_env = environment()
-test_env.set('DCONF_PROFILE', dconf_profile_full_path)
-test_env.set('GSETTINGS_SCHEMA_DIR', tracker_uninstalled_gsettings_schema_dir)
 
 tracker_uninstalled_testutils_dir = join_paths(meson.current_source_dir(), '..', '..', 'utils')
 test_env.prepend('PYTHONPATH', tracker_uninstalled_testutils_dir)
 
-test_env.set('TRACKER_DB_ONTOLOGIES_DIR', tracker_uninstalled_nepomuk_ontologies_dir)
-test_env.set('TRACKER_FUNCTIONAL_TEST_BUILD_DIR', build_root)
 test_env.set('TRACKER_FUNCTIONAL_TEST_CONFIG', config_json_full_path)
-test_env.set('TRACKER_LANGUAGE_STOP_WORDS_DIR', tracker_uninstalled_stop_words_dir)
-test_env.set('TRACKER_TEST_DOMAIN_ONTOLOGY_RULE', tracker_uninstalled_domain_rule)
 
 foreach t: functional_tests
-  test('functional-' + t, test_runner,
-    args: './' + t + '.py',
+  file = '@0@.py'.format(t)
+  test('functional-' + t, find_program(file),
     env: test_env,
     workdir: meson.current_source_dir(),
     timeout: 60)
 endforeach
 
-subdir('ipc')
+# FIXME
+#subdir('ipc')
diff --git a/tests/functional-tests/storetest.py b/tests/functional-tests/storetest.py
index ed7aa82c5..551bb617c 100644
--- a/tests/functional-tests/storetest.py
+++ b/tests/functional-tests/storetest.py
@@ -35,11 +35,19 @@ class CommonTrackerStoreTest (ut.TestCase):
 
     @classmethod
     def setUpClass(self):
-        extra_env = {'LC_COLLATE': 'en_GB.utf8'}
+        extra_env = cfg.test_environment()
+        extra_env['LC_COLLATE'] = 'en_GB.utf8'
 
-        self.tracker = trackertestutils.helpers.StoreHelper(cfg.TRACKER_STORE_PATH)
-        self.tracker.start(extra_env=extra_env)
+        self.sandbox = trackertestutils.helpers.TrackerDBusSandbox(
+            dbus_daemon_config_file=cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+        self.sandbox.start()
+
+        self.tracker = trackertestutils.helpers.StoreHelper(
+            self.sandbox.get_connection())
+        self.tracker.wait_for_ready()
+        self.tracker.start_watching_updates()
 
     @classmethod
     def tearDownClass(self):
-        self.tracker.stop()
+        self.tracker.stop_watching_updates()
+        self.sandbox.stop()
diff --git a/utils/trackertestutils/dbusdaemon.py b/utils/trackertestutils/dbusdaemon.py
index 0500369d6..ddf3fd136 100644
--- a/utils/trackertestutils/dbusdaemon.py
+++ b/utils/trackertestutils/dbusdaemon.py
@@ -16,8 +16,11 @@
 # 02110-1301, USA.
 
 
+from gi.repository import Gio
+
 import logging
 import os
+import signal
 import subprocess
 import threading
 
@@ -25,12 +28,17 @@ log = logging.getLogger(__name__)
 dbuslog = logging.getLogger(__name__ + '.dbus')
 
 
+class DaemonNotStartedError(Exception):
+    pass
+
+
 class DBusDaemon:
     """The private D-Bus instance that provides the sandbox's session bus.
 
     We support reading and writing the session information to a file. This
     means that if the user runs two sandbox instances on the same data
     directory at the same time, they will share the same message bus.
+
     """
 
     def __init__(self, session_file=None):
@@ -38,14 +46,18 @@ class DBusDaemon:
         self.existing_session = False
         self.process = None
 
-        try:
-            self.address, self.pid = self.read_session_file(session_file)
-            self.existing_session = True
-        except FileNotFoundError:
-            log.debug("No existing D-Bus session file was found.")
+        self.address = None
+        self.pid = None
+
+        self._gdbus_connection = None
+        self._previous_sigterm_handler = None
 
-            self.address = None
-            self.pid = None
+        if session_file:
+            try:
+                self.address, self.pid = self.read_session_file(session_file)
+                self.existing_session = True
+            except FileNotFoundError:
+                log.debug("No existing D-Bus session file was found.")
 
     def get_session_file(self):
         """Returns the path to the session file if we created it, or None."""
@@ -54,8 +66,15 @@ class DBusDaemon:
         return self.session_file
 
     def get_address(self):
+        if self.address is None:
+            raise DaemonNotStartedError()
         return self.address
 
+    def get_connection(self):
+        if self._gdbus_connection is None:
+            raise DaemonNotStartedError()
+        return self._gdbus_connection
+
     @staticmethod
     def read_session_file(session_file):
         with open(session_file, 'r') as f:
@@ -78,13 +97,21 @@ class DBusDaemon:
         with open(session_file, 'w') as f:
             f.write(content)
 
-    def start_if_needed(self):
+    def start_if_needed(self, config_file=None, env=None):
         if self.existing_session:
             log.debug('Using existing D-Bus session from file "%s" with address "%s"'
                       ' with PID %d' % (self.session_file, self.address, self.pid))
         else:
-            dbus_command = ['dbus-daemon', '--session', '--print-address=1', '--print-pid=1']
-            self.process = subprocess.Popen(dbus_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            dbus_command = ['dbus-daemon', '--print-address=1', '--print-pid=1']
+            if config_file:
+                dbus_command += ['--config-file=' + config_file]
+            else:
+                dbus_command += ['--session']
+            self.process = subprocess.Popen(
+                dbus_command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+            self._previous_sigterm_handler = signal.signal(
+                signal.SIGTERM, self._sigterm_handler)
 
             try:
                 self.address = self.process.stdout.readline().strip().decode('ascii')
@@ -96,8 +123,9 @@ class DBusDaemon:
             log.debug("Using new D-Bus session with address '%s' with PID %d",
                       self.address, self.pid)
 
-            self.write_session_file(self.session_file, self.address, self.pid)
-            log.debug("Wrote D-Bus session file at %s", self.session_file)
+            if self.session_file:
+                self.write_session_file(self.session_file, self.address, self.pid)
+                log.debug("Wrote D-Bus session file at %s", self.session_file)
 
             # We must read from the pipes continuously, otherwise the daemon
             # process will block.
@@ -106,11 +134,22 @@ class DBusDaemon:
             self._threads[0].start()
             self._threads[1].start()
 
+        self._gdbus_connection = Gio.DBusConnection.new_for_address_sync(
+            self.address,
+            Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT |
+            Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None)
+
+        log.debug("Pinging the new D-Bus daemon...")
+        self.ping_sync()
+
     def stop(self):
         if self.process:
             log.debug("  Stopping DBus daemon")
             self.process.terminate()
             self.process.wait()
+        if self._previous_sigterm_handler:
+            signal.signal(signal.SIGTERM, self._previous_sigterm_handler)
+            self._previous_sigterm_handler = None
 
     def pipe_to_log(self, pipe, source):
         """This function processes the output from our dbus-daemon instance."""
@@ -132,4 +171,10 @@ class DBusDaemon:
                 # itself, go here. Any g_print() messages also end up here.
                 dbuslog.debug(line)
 
+    def _sigterm_handler(self, signal, frame):
+        self.stop()
 
+    def ping_sync(self):
+        self._gdbus_connection.call_sync(
+            'org.freedesktop.DBus', '/', 'org.freedesktop.DBus', 'GetId',
+            None, None, Gio.DBusCallFlags.NONE, 10000, None)
diff --git a/utils/trackertestutils/helpers.py b/utils/trackertestutils/helpers.py
index 2b218e5d0..71ad68a88 100644
--- a/utils/trackertestutils/helpers.py
+++ b/utils/trackertestutils/helpers.py
@@ -24,9 +24,8 @@ from gi.repository import GLib
 import atexit
 import logging
 import os
-import subprocess
 
-from . import mainloop
+from . import dbusdaemon
 
 log = logging.getLogger(__name__)
 
@@ -54,171 +53,9 @@ def _cleanup_processes():
 atexit.register(_cleanup_processes)
 
 
-class Helper:
+class StoreHelper():
     """
-    Abstract helper for Tracker processes. Launches the process
-    and waits for it to appear on the session bus.
-
-    The helper will fail if the process is already running. Use
-    test-runner.sh to ensure the processes run inside a separate DBus
-    session bus.
-
-    The process is watched using a timed GLib main loop source. If the process
-    exits with an error code, the test will abort the next time the main loop
-    is entered (or straight away if currently running the main loop).
-    """
-
-    STARTUP_TIMEOUT = 200   # milliseconds
-    SHUTDOWN_TIMEOUT = 200  #
-
-    def __init__(self, helper_name, bus_name, process_path):
-        self.name = helper_name
-        self.bus_name = bus_name
-        self.process_path = process_path
-
-        self.log = logging.getLogger(f'{__name__}.{self.name}')
-
-        self.process = None
-        self.available = False
-
-        self.loop = mainloop.MainLoop()
-
-        self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
-
-    def _start_process(self, command_args=None, extra_env=None):
-        global _process_list
-        _process_list.append(self)
-
-        command = [self.process_path] + (command_args or [])
-        self.log.debug("Starting %s.", ' '.join(command))
-
-        env = os.environ
-        if extra_env:
-            self.log.debug("  starting with extra environment: %s", extra_env)
-            env.update(extra_env)
-
-        try:
-            return subprocess.Popen(command, env=env)
-        except OSError as e:
-            raise RuntimeError("Error starting %s: %s" % (self.process_path, e))
-
-    def _bus_name_appeared(self, connection, name, owner):
-        self.log.debug("%s appeared on the message bus, owned by %s", name, owner)
-        self.available = True
-        self.loop.quit()
-
-    def _bus_name_vanished(self, connection, name):
-        self.log.debug("%s vanished from the message bus", name)
-        self.available = False
-        self.loop.quit()
-
-    def _process_watch_cb(self):
-        if self.process_watch_timeout == 0:
-            # GLib seems to call the timeout after we've removed it
-            # sometimes, which causes errors unless we detect it.
-            return False
-
-        status = self.process.poll()
-
-        if status is None:
-            return True    # continue
-        elif status == 0 and not self.abort_if_process_exits_with_status_0:
-            return True    # continue
-        else:
-            self.process_watch_timeout = 0
-            raise RuntimeError(f"{self.name} exited with status: {self.status}")
-
-    def _process_startup_timeout_cb(self):
-        self.log.debug(f"Process timeout of {self.STARTUP_TIMEOUT}ms was called")
-        self.loop.quit()
-        self.timeout_id = None
-        return False
-
-    def start(self, command_args=None, extra_env=None):
-        """
-        Start an instance of process and wait for it to appear on the bus.
-        """
-        if self.process is not None:
-            raise RuntimeError("%s: already started" % self.name)
-
-        self._bus_name_watch_id = Gio.bus_watch_name_on_connection(
-            self.bus, self.bus_name, Gio.BusNameWatcherFlags.NONE,
-            self._bus_name_appeared, self._bus_name_vanished)
-
-        # We expect the _bus_name_vanished callback to be called here,
-        # causing the loop to exit again.
-        self.loop.run_checked()
-
-        if self.available:
-            # It's running, but we didn't start it...
-            raise RuntimeError("Unable to start test instance of %s: "
-                               "already running" % self.name)
-
-        self.process = self._start_process(command_args=command_args,
-                                           extra_env=extra_env)
-        self.log.debug('Started with PID %i', self.process.pid)
-
-        self.process_startup_timeout = GLib.timeout_add(
-            self.STARTUP_TIMEOUT, self._process_startup_timeout_cb)
-
-        self.abort_if_process_exits_with_status_0 = True
-
-        # Run the loop until the bus name appears, or the process dies.
-        self.loop.run_checked()
-
-        self.abort_if_process_exits_with_status_0 = False
-
-    def stop(self):
-        global _process_list
-
-        if self.process is None:
-            # Seems that it didn't even start...
-            return
-
-        if self.process.poll() == None:
-            GLib.source_remove(self.process_startup_timeout)
-            self.process_startup_timeout = 0
-
-            self.process.terminate()
-            returncode = self.process.wait(timeout=self.SHUTDOWN_TIMEOUT * 1000)
-            if returncode is None:
-                self.log.debug("Process failed to terminate in time, sending kill!")
-                self.process.kill()
-                self.process.wait()
-            elif returncode > 0:
-                self.log.warn("Process returned error code %s", returncode)
-
-        self.log.debug("Process stopped.")
-
-        # Run the loop to handle the expected name_vanished signal.
-        self.loop.run_checked()
-        Gio.bus_unwatch_name(self._bus_name_watch_id)
-
-        self.process = None
-        _process_list.remove(self)
-
-    def kill(self):
-        global _process_list
-
-        if self.process_watch_timeout != 0:
-            GLib.source_remove(self.process_watch_timeout)
-            self.process_watch_timeout = 0
-
-        self.process.kill()
-
-        # Name owner changed callback should take us out from this loop
-        self.loop.run_checked()
-        Gio.bus_unwatch_name(self._bus_name_watch_id)
-
-        self.process = None
-        _process_list.remove(self)
-
-        self.log.debug("Process killed.")
-
-
-class StoreHelper (Helper):
-    """
-    Helper for starting and testing the tracker-store daemon.
+    Helper for testing the tracker-store daemon.
     """
 
     TRACKER_BUSNAME = 'org.freedesktop.Tracker1'
@@ -234,32 +71,33 @@ class StoreHelper (Helper):
     TRACKER_STATUS_OBJ_PATH = "/org/freedesktop/Tracker1/Status"
     STATUS_IFACE = "org.freedesktop.Tracker1.Status"
 
-    def __init__(self, process_path):
-        Helper.__init__(self, "tracker-store", self.TRACKER_BUSNAME, process_path)
+    def __init__(self, dbus_connection):
+        self.log = logging.getLogger(__name__)
 
-    def start(self, command_args=None, extra_env=None):
-        Helper.start(self, command_args, extra_env)
+        self.bus = dbus_connection
 
         self.resources = Gio.DBusProxy.new_sync(
-            self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+            self.bus, Gio.DBusProxyFlags.NONE, None,
             self.TRACKER_BUSNAME, self.TRACKER_OBJ_PATH, self.RESOURCES_IFACE)
 
         self.backup_iface = Gio.DBusProxy.new_sync(
-            self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+            self.bus, Gio.DBusProxyFlags.NONE, None,
             self.TRACKER_BUSNAME, self.TRACKER_BACKUP_OBJ_PATH, self.BACKUP_IFACE)
 
         self.stats_iface = Gio.DBusProxy.new_sync(
-            self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+            self.bus, Gio.DBusProxyFlags.NONE, None,
             self.TRACKER_BUSNAME, self.TRACKER_STATS_OBJ_PATH, self.STATS_IFACE)
 
         self.status_iface = Gio.DBusProxy.new_sync(
-            self.bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None,
+            self.bus, Gio.DBusProxyFlags.NONE, None,
             self.TRACKER_BUSNAME, self.TRACKER_STATUS_OBJ_PATH, self.STATUS_IFACE)
 
+    def wait_for_ready(self):
         self.log.debug("Calling %s.Wait() method", self.STATUS_IFACE)
         self.status_iface.Wait()
         self.log.debug("Ready")
 
+    def start_watching_updates(self):
         self.reset_graph_updates_tracking()
 
         def signal_handler(proxy, sender_name, signal_name, parameters):
@@ -269,9 +107,7 @@ class StoreHelper (Helper):
         self.graph_updated_handler_id = self.resources.connect(
             'g-signal', signal_handler)
 
-    def stop(self):
-        Helper.stop(self)
-
+    def stop_watching_updates(self):
         if self.graph_updated_handler_id != 0:
             self.resources.disconnect(self.graph_updated_handler_id)
 
@@ -581,3 +417,29 @@ class StoreHelper (Helper):
             return False
         else:
             raise Exception("Something fishy is going on")
+
+
+class TrackerDBusSandbox:
+    """
+    Private D-Bus session bus which executes a sandboxed Tracker instance.
+
+    """
+    def __init__(self, dbus_daemon_config_file, extra_env=None):
+        self.dbus_daemon_config_file = dbus_daemon_config_file
+        self.extra_env = extra_env or {}
+
+        self.daemon = dbusdaemon.DBusDaemon()
+
+    def start(self):
+        env = os.environ
+        env.update(self.extra_env)
+
+        logging.info("Starting D-Bus daemon for sandbox.")
+        logging.debug("Added environment variables: %s", self.extra_env)
+        self.daemon.start_if_needed(self.dbus_daemon_config_file, env=env)
+
+    def stop(self):
+        self.daemon.stop()
+
+    def get_connection(self):
+        return self.daemon.get_connection()


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]