Commit dae63fea authored by Sam Thursfield's avatar Sam Thursfield

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.

Some test code is changed to support the new model.
parent 2d9c4449
......@@ -283,10 +283,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'))
configure_file(input: 'config.h.meson.in',
output: 'config.h',
configuration: conf)
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......@@ -111,21 +109,18 @@ class TrackerStoreSparqlBugsTests (CommonTrackerStoreTest):
"""
results1 = self.tracker.query(query1)
print("1", results1)
self.assertEqual(len(results1), 1)
self.assertEqual(len(results1[0]), 2)
self.assertEqual(results1[0][0], "contact:test")
self.assertEqual(results1[0][1], "98653")
results2 = self.tracker.query(query2)
print("2", results2)
self.assertEqual(len(results2), 1)
self.assertEqual(len(results2[0]), 2)
self.assertEqual(results2[0][0], "contact:test")
self.assertEqual(results2[0][1], "98653")
results3 = self.tracker.query(query3)
print("3", results3)
self.assertEqual(len(results3), 1)
self.assertEqual(len(results3[0]), 2)
self.assertEqual(results3[0][0], "contact:test")
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......@@ -52,7 +50,7 @@ class TrackerStoreSignalsTests (CommonTrackerStoreTest):
self.loop = GLib.MainLoop()
self.timeout_id = 0
self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
self.bus = self.sandbox.get_connection()
self.results_classname = None
self.results_deletes = None
......@@ -125,7 +123,6 @@ class TrackerStoreSignalsTests (CommonTrackerStoreTest):
"""
self.__connect_signal()
self.tracker.update(CONTACT)
time.sleep(1)
self.__wait_for_signal()
# validate results
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
......
#!/usr/bin/python3
#
# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
#
......@@ -26,8 +24,8 @@ changes and checking if the data is still there.
from gi.repository import GLib
import logging
import os
import pathlib
import shutil
import re
import tempfile
......@@ -48,101 +46,6 @@ XSD_INTEGER = "http://www.w3.org/2001/XMLSchema#integer"
TEST_PREFIX = "http://example.org/ns#"
TEST_ENV_VARS = {"LC_COLLATE": "en_GB.utf8"}
REASONABLE_TIMEOUT = 5
log = logging.getLogger()
class UnableToBootException (Exception):
pass
class TrackerSystemAbstraction (object):
def __init__(self, settings=None):
self.store = None
self._dirs = {}
def xdg_data_home(self):
return os.path.join(self._basedir, 'data')
def xdg_cache_home(self):
return os.path.join(self._basedir, 'cache')
def set_up_environment(self, settings=None, ontodir=None):
"""
Sets up the XDG_*_HOME variables and make sure the directories exist
Settings should be a dict mapping schema names to dicts that hold the
settings that should be changed in those schemas. The contents dicts
should map key->value, where key is a key name and value is a suitable
GLib.Variant instance.
"""
self._basedir = tempfile.mkdtemp()
self._dirs = {
"XDG_DATA_HOME": self.xdg_data_home(),
"XDG_CACHE_HOME": self.xdg_cache_home()
}
for var, directory in list(self._dirs.items()):
os.makedirs(directory)
os.makedirs(os.path.join(directory, 'tracker'))
os.environ[var] = directory
if ontodir:
log.debug("export %s=%s", "TRACKER_DB_ONTOLOGIES_DIR", ontodir)
os.environ["TRACKER_DB_ONTOLOGIES_DIR"] = ontodir
for var, value in TEST_ENV_VARS.items():
log.debug("export %s=%s", var, value)
os.environ[var] = value
# Previous loop should have set DCONF_PROFILE to the test location
if settings is not None:
self._apply_settings(settings)
def _apply_settings(self, settings):
for schema_name, contents in settings.items():
dconf = trackertestutils.dconf.DConfClient(schema_name)
dconf.reset()
for key, value in contents.items():
dconf.write(key, value)
def tracker_store_testing_start(self, confdir=None, ontodir=None):
"""
Stops any previous instance of the store, calls set_up_environment,
and starts a new instances of the store
"""
self.set_up_environment(confdir, ontodir)
self.store = trackertestutils.helpers.StoreHelper(cfg.TRACKER_STORE_PATH)
self.store.start()
def tracker_store_restart_with_new_ontologies(self, ontodir):
self.store.stop()
if ontodir:
os.environ["TRACKER_DB_ONTOLOGIES_DIR"] = ontodir
try:
self.store.start()
except GLib.Error:
raise UnableToBootException(
"Unable to boot the store \n(" + str(e) + ")")
def finish(self):
"""
Stop all running processes and remove all test data.
"""
if self.store:
self.store.stop()
for path in list(self._dirs.values()):
shutil.rmtree(path)
os.rmdir(self._basedir)
class OntologyChangeTestTemplate (ut.TestCase):
"""
......@@ -158,35 +61,43 @@ class OntologyChangeTestTemplate (ut.TestCase):
Check doc in those methods for the specific details.
"""
def get_ontology_dir(self, param):
return os.path.join(cfg.TEST_ONTOLOGIES_DIR, param)
def setUp(self):
self.system = TrackerSystemAbstraction()
self.tmpdir = tempfile.mkdtemp(prefix='tracker-test-')
def tearDown(self):
self.system.finish()
shutil.rmtree(self.tmpdir, ignore_errors=True)
def template_test_ontology_change(self):
def get_ontology_dir(self, param):
return str(pathlib.Path(__file__).parent.joinpath('test-ontologies', param))
def template_test_ontology_change(self):
self.set_ontology_dirs()
basic_ontologies = self.get_ontology_dir(self.FIRST_ONTOLOGY_DIR)
modified_ontologies = self.get_ontology_dir(self.SECOND_ONTOLOGY_DIR)
self.__assert_ontology_dates(self.FIRST_ONTOLOGY_DIR, self.SECOND_ONTOLOGY_DIR)
self.__assert_ontology_dates(basic_ontologies, modified_ontologies)
extra_env = cfg.test_environment(self.tmpdir)
extra_env['LC_COLLATE'] = 'en_GB.utf8'
extra_env['TRACKER_DB_ONTOLOGIES_DIR'] = self.get_ontology_dir(self.FIRST_ONTOLOGY_DIR)
self.system.tracker_store_testing_start(ontodir=basic_ontologies)
self.tracker = self.system.store
sandbox1 = trackertestutils.helpers.TrackerDBusSandbox(
cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
sandbox1.start()
self.tracker = trackertestutils.helpers.StoreHelper(sandbox1.get_connection())
self.tracker.start_and_wait_for_ready()
self.insert_data()
try:
# Boot the second set of ontologies
self.system.tracker_store_restart_with_new_ontologies(
modified_ontologies)
except UnableToBootException as e:
self.fail(str(self.__class__) + " " + str(e))
sandbox1.stop()
# Boot the second set of ontologies
extra_env['TRACKER_DB_ONTOLOGIES_DIR'] = self.get_ontology_dir(self.SECOND_ONTOLOGY_DIR)
sandbox2 = trackertestutils.helpers.TrackerDBusSandbox(
cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
sandbox2.start()
self.tracker = trackertestutils.helpers.StoreHelper(sandbox2.get_connection())
self.tracker.start_and_wait_for_ready()
self.validate_status()
......@@ -233,7 +144,7 @@ class OntologyChangeTestTemplate (ut.TestCase):
(member, dbus_result))
return
def __assert_ontology_dates(self, first_dir, second_dir):
def __assert_ontology_dates(self, first, second):
"""
Asserts that 91-test.ontology in second_dir has a more recent
modification time than in first_dir
......@@ -241,23 +152,24 @@ class OntologyChangeTestTemplate (ut.TestCase):
ISO9601_REGEX = "(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)"
def get_ontology_date(ontology):
for line in open(ontology, 'r'):
if "nao:lastModified" in line:
getmodtime = re.compile(
'nao:lastModified\ \"' + ISO9601_REGEX + '\"')
modtime_match = getmodtime.search(line)
if (modtime_match):
nao_date = modtime_match.group(1)
return time.strptime(nao_date, "%Y-%m-%dT%H:%M:%SZ")
else:
print("something funky in", line)
break
with open(ontology, 'r') as f:
for line in f:
if "nao:lastModified" in line:
getmodtime = re.compile(
'nao:lastModified\ \"' + ISO9601_REGEX + '\"')
modtime_match = getmodtime.search(line)
if (modtime_match):
nao_date = modtime_match.group(1)
return time.strptime(nao_date, "%Y-%m-%dT%H:%M:%SZ")
else:
print("something funky in", line)
break
first_date = get_ontology_date(
os.path.join(first_dir, "91-test.ontology"))
os.path.join(self.get_ontology_dir(first), "91-test.ontology"))
second_date = get_ontology_date(
os.path.join(second_dir, "91-test.ontology"))
os.path.join(self.get_ontology_dir(second), "91-test.ontology"))
if first_date >= second_date:
self.fail("nao:modifiedTime in '%s' is not more recent in the second ontology" % (
"91-test.ontology"))
......
{
"TEST_ONTOLOGIES_DIR": "@FUNCTIONAL_TESTS_ONTOLOGIES_DIR@",
"TRACKER_STORE_PATH": "@FUNCTIONAL_TESTS_TRACKER_STORE_PATH@"
"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@"
}
......@@ -18,10 +18,10 @@
# 02110-1301, USA.
#
import json
import logging
import os
import pathlib
import sys
......@@ -34,12 +34,21 @@ 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 = config['TEST_ONTOLOGIES_DIR']
TRACKER_STORE_PATH = config['TRACKER_STORE_PATH']
def test_environment(tmpdir):
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'],
'XDG_CACHE_HOME': os.path.join(tmpdir, 'cache'),
'XDG_CONFIG_HOME': os.path.join(tmpdir, 'config'),
'XDG_DATA_HOME': os.path.join(tmpdir, 'data'),
'XDG_RUNTIME_DIR': os.path.join(tmpdir, 'run'),
}
def get_environment_boolean(variable):
......@@ -55,5 +64,24 @@ def get_environment_boolean(variable):
(variable, value))
def get_environment_int(variable, default=0):
try:
return int(os.environ.get(variable))
except (TypeError, ValueError):
return default
if get_environment_boolean('TRACKER_TESTS_VERBOSE'):
# Output all logs to stderr
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
else:
# Output some messages from D-Bus daemon to stderr by default. In practice,
# only errors and warnings should be output here unless the environment
# contains G_MESSAGES_DEBUG= and/or TRACKER_VERBOSITY=1 or more.
handler_stderr = logging.StreamHandler(stream=sys.stderr)
handler_stderr.addFilter(logging.Filter('trackertestutils.dbusdaemon.stderr'))
handler_stdout = logging.StreamHandler(stream=sys.stderr)
handler_stdout.addFilter(logging.Filter('trackertestutils.dbusdaemon.stdout'))
logging.basicConfig(level=logging.INFO,
handlers=[handler_stderr, handler_stdout],
format='%(message)s')
test_runner = configure_file(
input: 'test-runner.sh.in',
output: 'test-runner.sh',
configuration: conf)
test_runner = find_program(test_runner)
python = find_program('python3')
# 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.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,21 @@ 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, python,
args: [file],
env: test_env,
workdir: meson.current_source_dir(),
timeout: 60)
endforeach
subdir('ipc')
# FIXME
#subdir('ipc')
......@@ -19,6 +19,8 @@
#
import os
import shutil
import tempfile
import time
import unittest as ut
......@@ -35,11 +37,28 @@ class CommonTrackerStoreTest (ut.TestCase):
@classmethod
def setUpClass(self):
extra_env = {'LC_COLLATE': 'en_GB.utf8'}
self.tmpdir = tempfile.mkdtemp(prefix='tracker-test-')
self.tracker = trackertestutils.helpers.StoreHelper(cfg.TRACKER_STORE_PATH)
self.tracker.start(extra_env=extra_env)
try:
extra_env = cfg.test_environment(self.tmpdir)
extra_env['LANG'] = 'en_GB.utf8'
extra_env['LC_COLLATE'] = 'en_GB.utf8'
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.start_and_wait_for_ready()
self.tracker.start_watching_updates()
except Exception as e:
shutil.rmtree(self.tmpdir, ignore_errors=True)
raise
@classmethod
def tearDownClass(self):
self.tracker.stop()
self.tracker.stop_watching_updates()
self.sandbox.stop()
shutil.rmtree(self.tmpdir, ignore_errors=True)
......@@ -7,6 +7,7 @@
<listen>unix:tmpdir=./</listen>
<servicedir>@abs_top_builddir@/tests/services/</servicedir>
<standard_session_servicedirs/>
<policy context="default">
<!-- Allow everything to be sent -->
......
......@@ -24,6 +24,7 @@
#
import argparse
import configparser
import locale
import logging
import os
......@@ -33,10 +34,10 @@ import subprocess
import sys
import threading
import configparser
from gi.repository import GLib
import trackertestutils.dbusdaemon
# Script
script_name = 'tracker-sandbox'
script_version = '1.0'
......@@ -84,116 +85,6 @@ log = logging.getLogger('sandbox')
dbuslog = logging.getLogger('dbus')
# Private DBus daemon
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):
self.session_file = session_file
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
def get_session_file(self):
"""Returns the path to the session file if we created it, or None."""
if self.existing_session:
return None
return self.session_file
def get_address(self):
return self.address
@staticmethod
def read_session_file(session_file):
with open(session_file, 'r') as f:
content = f.read()
try:
address = content.splitlines()[0]
pid = int(content.splitlines()[1])
except ValueError:
raise RuntimeError(f"D-Bus session file {session_file} is not valid. "
"Remove this file to start a new session.")
return address, pid
@staticmethod
def write_session_file(session_file, address, pid):
os.makedirs(os.path.dirname(session_file), exist_ok=True)
content = '%s\n%s' % (address, pid)
with open(session_file, 'w') as f:
f.write(content)
def start_if_needed(self):
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)
try:
self.address = self.process.stdout.readline().strip().decode('ascii')
self.pid = int(self.process.stdout.readline().strip().decode('ascii'))
except ValueError:
error = self.process.stderr.read().strip().decode('unicode-escape')
raise RuntimeError(f"Failed to start D-Bus daemon.\n{error}")
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)
# 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, 'stdout'), daemon=True),
threading.Thread(target=self.pipe_to_log, args=(self.process.stderr, 'stderr'), daemon=True)]
self._threads[0].start()
self._threads[1].start()
def stop(self):
if self.process:
log.debug(" Stopping DBus daemon")
self.process.terminate()
self.process.wait()
def pipe_to_log(self, pipe, source):
"""This function processes the output from our dbus-daemon instance."""
while True:
line_raw = pipe.readline()
if len(line_raw) == 0:
break
line = line_raw.decode('utf-8').rstrip()
if line.startswith('(tracker-'):
# We set G_MESSAGES_PREFIXED=all, meaning that all log messages
# output by Tracker processes have a prefix. Note that
# g_print() will NOT be captured here.
dbuslog.info(line)
else:
# Log messages from other daemons, including the dbus-daemon
# itself, go here. Any g_print() messages also end up here.
dbuslog.debug(line)
# Environment / Clean up
def environment_unset(dbus):
......@@ -260,7 +151,7 @@ def environment_set(index_location, prefix, verbosity=0):
dbus_session_file = os.path.join(
os.environ['XDG_RUNTIME_DIR'], 'dbus-session')
dbus = DBusDaemon(dbus_session_file)
dbus = trackertestutils.dbusdaemon.DBusDaemon(dbus_session_file)
dbus.start_if_needed()
# Important, other subprocesses must use our new bus
......
# Copyright (C) 2018,2019, 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
from gi.repository import Gio
import logging
import os
import signal
import subprocess
import threading
log = logging.getLogger(__name__)
dbus_stderr_log = logging.getLogger(__name__ + '.stderr')
dbus_stdout_log = logging.getLogger(__name__ + '.stdout')
class DaemonNotStartedError(Exception):