[tracker/sam/tracker-2.3-developer-experience: 9/38] functional-tests: Terminate Tracker processes before the message bus
- From: Sam Thursfield <sthursfield src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/sam/tracker-2.3-developer-experience: 9/38] functional-tests: Terminate Tracker processes before the message bus
- Date: Wed, 2 Oct 2019 20:18:04 +0000 (UTC)
commit 12efd3258ec3fa4b5c1a3bfc9ca65e7761384c18
Author: Sam Thursfield <sam afuera me uk>
Date: Tue Sep 10 12:14:35 2019 +0200
functional-tests: Terminate Tracker processes before the message bus
This reduces the volume of criticals like this that we see in the log
output:
(tracker-miner-fs:14955): GLib-GIO-CRITICAL **: 11:38:40.386: Error while sending AddMatch() message:
The connection is closed
I did still see one test that output a bunch of similar criticals from
tracker-extract.
utils/trackertestutils/dbusdaemon.py | 24 +++++++++
utils/trackertestutils/helpers.py | 25 +++++++++
utils/trackertestutils/meson.build | 3 +-
utils/trackertestutils/psutil_mini.py | 98 +++++++++++++++++++++++++++++++++++
4 files changed, 149 insertions(+), 1 deletion(-)
---
diff --git a/utils/trackertestutils/dbusdaemon.py b/utils/trackertestutils/dbusdaemon.py
index 43fe8f146..c7e4707f3 100644
--- a/utils/trackertestutils/dbusdaemon.py
+++ b/utils/trackertestutils/dbusdaemon.py
@@ -17,6 +17,7 @@
from gi.repository import Gio
+from gi.repository import GLib
import logging
import os
@@ -193,6 +194,29 @@ class DBusDaemon:
self.stop()
def ping_sync(self):
+ """Call the daemon Ping() method to check that it is alive."""
self._gdbus_connection.call_sync(
'org.freedesktop.DBus', '/', 'org.freedesktop.DBus', 'GetId',
None, None, Gio.DBusCallFlags.NONE, 10000, None)
+
+ def list_names_sync(self):
+ """Get the name of every client connected to the bus."""
+ conn = self.get_connection()
+ result = conn.call_sync('org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus', 'ListNames', None,
+ GLib.VariantType('(as)'),
+ Gio.DBusCallFlags.NONE, -1, None)
+ return result[0]
+
+ def get_connection_unix_process_id_sync(self, name):
+ """Get the process ID for one of the names connected to the bus."""
+ conn = self.get_connection()
+ result = conn.call_sync('org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus',
+ 'GetConnectionUnixProcessID',
+ GLib.Variant('(s)', [name]),
+ GLib.VariantType('(u)'),
+ Gio.DBusCallFlags.NONE, -1, None)
+ return result[0]
diff --git a/utils/trackertestutils/helpers.py b/utils/trackertestutils/helpers.py
index 45fa67242..fc81b52e6 100644
--- a/utils/trackertestutils/helpers.py
+++ b/utils/trackertestutils/helpers.py
@@ -24,9 +24,12 @@ from gi.repository import GLib
import atexit
import logging
import os
+import signal
from . import dbusdaemon
from . import mainloop
+from . import psutil_mini as psutil
+
log = logging.getLogger(__name__)
@@ -463,6 +466,28 @@ class TrackerDBusSandbox:
self.daemon.start_if_needed(self.dbus_daemon_config_file, env=env)
def stop(self):
+ tracker_processes = []
+
+ log.info("Looking for active Tracker processes on the bus")
+ for busname in self.daemon.list_names_sync():
+ if busname.startswith('org.freedesktop.Tracker1'):
+ pid = self.daemon.get_connection_unix_process_id_sync(busname)
+ tracker_processes.append(pid)
+
+ log.info("Terminating %i Tracker processes", len(tracker_processes))
+ for pid in tracker_processes:
+ os.kill(pid, signal.SIGTERM)
+
+ log.info("Waiting for %i Tracker processes", len(tracker_processes))
+ for pid in tracker_processes:
+ psutil.wait_pid(pid)
+
+ # We need to wait until Tracker processes have stopped before we
+ # terminate the D-Bus daemon, otherwise lots of criticals like this
+ # appear in the log output:
+ #
+ # (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()
diff --git a/utils/trackertestutils/meson.build b/utils/trackertestutils/meson.build
index a144794d5..e8ab94c72 100644
--- a/utils/trackertestutils/meson.build
+++ b/utils/trackertestutils/meson.build
@@ -3,7 +3,8 @@ sources = [
'dbusdaemon.py',
'dconf.py',
'helpers.py',
- 'mainloop.py'
+ 'mainloop.py',
+ 'psutil_mini.py',
]
install_data(sources,
diff --git a/utils/trackertestutils/psutil_mini.py b/utils/trackertestutils/psutil_mini.py
new file mode 100644
index 000000000..d0c93565d
--- /dev/null
+++ b/utils/trackertestutils/psutil_mini.py
@@ -0,0 +1,98 @@
+# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found at https://github.com/giampaolo/psutil/blob/master/LICENSE
+#
+# Taken from https://github.com/giampaolo/psutil/blob/master/psutil/_psposix.py
+# by Sam Thursfield to avoid adding a dependency between the Tracker testsuite
+# and the 'psutil' module.
+
+
+import os
+import time
+
+
+class TimeoutExpired(Exception):
+ pass
+
+
+def pid_exists(pid):
+ """Check whether pid exists in the current process table."""
+ if pid == 0:
+ # According to "man 2 kill" PID 0 has a special meaning:
+ # it refers to <<every process in the process group of the
+ # calling process>> so we don't want to go any further.
+ # If we get here it means this UNIX platform *does* have
+ # a process with id 0.
+ return True
+ try:
+ os.kill(pid, 0)
+ except ProcessLookupError:
+ return False
+ except PermissionError:
+ # EPERM clearly means there's a process to deny access to
+ return True
+ # According to "man 2 kill" possible error values are
+ # (EINVAL, EPERM, ESRCH)
+ else:
+ return True
+
+
+def wait_pid(pid, timeout=None, proc_name=None):
+ """Wait for process with pid 'pid' to terminate and return its
+ exit status code as an integer.
+ If pid is not a children of os.getpid() (current process) just
+ waits until the process disappears and return None.
+ If pid does not exist at all return None immediately.
+ Raise TimeoutExpired on timeout expired.
+ """
+ def check_timeout(delay):
+ if timeout is not None:
+ if timer() >= stop_at:
+ raise TimeoutExpired(timeout, pid=pid, name=proc_name)
+ time.sleep(delay)
+ return min(delay * 2, 0.04)
+
+ timer = getattr(time, 'monotonic', time.time)
+ if timeout is not None:
+ def waitcall():
+ return os.waitpid(pid, os.WNOHANG)
+ stop_at = timer() + timeout
+ else:
+ def waitcall():
+ return os.waitpid(pid, 0)
+
+ delay = 0.0001
+ while True:
+ try:
+ retpid, status = waitcall()
+ except InterruptedError:
+ delay = check_timeout(delay)
+ except ChildProcessError:
+ # This has two meanings:
+ # - pid is not a child of os.getpid() in which case
+ # we keep polling until it's gone
+ # - pid never existed in the first place
+ # In both cases we'll eventually return None as we
+ # can't determine its exit status code.
+ while True:
+ if pid_exists(pid):
+ delay = check_timeout(delay)
+ else:
+ return
+ else:
+ if retpid == 0:
+ # WNOHANG was used, pid is still running
+ delay = check_timeout(delay)
+ continue
+ # process exited due to a signal; return the integer of
+ # that signal
+ if os.WIFSIGNALED(status):
+ return -os.WTERMSIG(status)
+ # process exited using exit(2) system call; return the
+ # integer exit(2) system call has been called with
+ elif os.WIFEXITED(status):
+ return os.WEXITSTATUS(status)
+ else:
+ # should never happen
+ raise ValueError("unknown process exit status %r" % status)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]