[tracker-miners/sam/umockdev: 3/3] functional-tests: Add miner-power test
- From: Sam Thursfield <sthursfield src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker-miners/sam/umockdev: 3/3] functional-tests: Add miner-power test
- Date: Fri, 22 May 2020 20:03:48 +0000 (UTC)
commit aaede85488744f32ecaec55029683e6d63ef43fe
Author: Sam Thursfield <sam afuera me uk>
Date: Thu May 21 23:41:07 2020 +0200
functional-tests: Add miner-power test
This test uses [umockdev] to verify that the miner-fs pauses when upower
reports a low battery condition.
The umockdev dependency is optional and the test will report as being
skipped if umockdev isn't available.
Depends on https://gitlab.gnome.org/GNOME/tracker-oci-images/-/merge_requests/23
and https://gitlab.gnome.org/GNOME/tracker/-/merge_requests/254
[umockdev]: https://github.com/martinpitt/umockdev
meson.build | 3 +
src/libtracker-miner/tracker-miner-object.c | 6 +-
tests/functional-tests/configuration.json.in | 3 +-
tests/functional-tests/configuration.py | 3 +-
tests/functional-tests/fixtures.py | 9 +-
tests/functional-tests/meson.build | 25 +++-
tests/functional-tests/miner-power.py | 85 ++++++++++++
tests/functional-tests/minerhelper.py | 147 +++++++++++----------
tests/meson.build | 4 +-
.../{test-bus.conf.in => test-session-bus.conf.in} | 0
tests/test-system-bus.conf | 20 +++
11 files changed, 227 insertions(+), 78 deletions(-)
---
diff --git a/meson.build b/meson.build
index 89e1e24bb..55409fb5d 100644
--- a/meson.build
+++ b/meson.build
@@ -93,6 +93,9 @@ libmath = cc.find_library('m', required: false)
network_manager = dependency('libnm', required: get_option('network_manager'))
have_network_manager = network_manager.found()
+# We use this in the functional-tests if available.
+umockdev = dependency('umockdev-1.0', required: false)
+
have_tracker_extract = get_option('extract')
have_tracker_miner_fs = get_option('miner_fs')
have_tracker_miner_rss = get_option('miner_rss')
diff --git a/src/libtracker-miner/tracker-miner-object.c b/src/libtracker-miner/tracker-miner-object.c
index 733b54f2a..bd99273e5 100644
--- a/src/libtracker-miner/tracker-miner-object.c
+++ b/src/libtracker-miner/tracker-miner-object.c
@@ -468,7 +468,11 @@ miner_get_property (GObject *object,
switch (prop_id) {
case PROP_STATUS:
- g_value_set_string (value, miner->priv->status);
+ if (tracker_miner_is_paused (miner)) {
+ g_value_set_string (value, "Paused");
+ } else {
+ g_value_set_string (value, miner->priv->status);
+ }
break;
case PROP_PROGRESS:
g_value_set_double (value, miner->priv->progress);
diff --git a/tests/functional-tests/configuration.json.in b/tests/functional-tests/configuration.json.in
index 7ef895fe7..a4cb8aa33 100644
--- a/tests/functional-tests/configuration.json.in
+++ b/tests/functional-tests/configuration.json.in
@@ -1,5 +1,6 @@
{
- "TEST_DBUS_DAEMON_CONFIG_FILE": "@TEST_DBUS_DAEMON_CONFIG_FILE@",
+ "TEST_SESSION_BUS_CONFIG_FILE": "@TEST_SESSION_BUS_CONFIG_FILE@",
+ "TEST_SYSTEM_BUS_CONFIG_FILE": "@TEST_SYSTEM_BUS_CONFIG_FILE@",
"TEST_DCONF_PROFILE": "@TEST_DCONF_PROFILE@",
"TEST_DOMAIN_ONTOLOGY_RULE": "@TEST_DOMAIN_ONTOLOGY_RULE@",
"TEST_EXTRACTOR_RULES_DIR": "@TEST_EXTRACTOR_RULES_DIR@",
diff --git a/tests/functional-tests/configuration.py b/tests/functional-tests/configuration.py
index 3f3d96c0e..d4d9d5452 100644
--- a/tests/functional-tests/configuration.py
+++ b/tests/functional-tests/configuration.py
@@ -42,7 +42,8 @@ with open(os.environ['TRACKER_FUNCTIONAL_TEST_CONFIG']) as f:
config = json.load(f)
-TEST_DBUS_DAEMON_CONFIG_FILE = config['TEST_DBUS_DAEMON_CONFIG_FILE']
+TEST_SESSION_BUS_CONFIG_FILE = config['TEST_SESSION_BUS_CONFIG_FILE']
+TEST_SYSTEM_BUS_CONFIG_FILE = config['TEST_SYSTEM_BUS_CONFIG_FILE']
TRACKER_EXTRACT_PATH = config['TRACKER_EXTRACT_PATH']
diff --git a/tests/functional-tests/fixtures.py b/tests/functional-tests/fixtures.py
index e9fe1c6cc..f02e04f13 100644
--- a/tests/functional-tests/fixtures.py
+++ b/tests/functional-tests/fixtures.py
@@ -60,9 +60,9 @@ def tracker_test_main():
# 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_stderr.addFilter(logging.Filter('sandbox-session-bus.stderr'))
handler_stdout = logging.StreamHandler(stream=sys.stderr)
- handler_stdout.addFilter(logging.Filter('trackertestutils.dbusdaemon.stdout'))
+ handler_stdout.addFilter(logging.Filter('sandbox-session-bus.stdout'))
logging.basicConfig(level=logging.INFO,
handlers=[handler_stderr, handler_stdout],
format='%(message)s')
@@ -96,7 +96,7 @@ class TrackerMinerTest(ut.TestCase):
extra_env['LANG'] = 'en_GB.utf8'
self.sandbox = trackertestutils.helpers.TrackerDBusSandbox(
- dbus_daemon_config_file=cfg.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+ session_bus_config_file=cfg.TEST_SESSION_BUS_CONFIG_FILE, extra_env=extra_env)
self.sandbox.start()
@@ -109,9 +109,8 @@ class TrackerMinerTest(ut.TestCase):
self.sandbox.set_config(self.config())
- self.miner_fs = MinerFsHelper(self.sandbox.get_connection())
+ self.miner_fs = MinerFsHelper(self.sandbox.get_session_bus_connection())
self.miner_fs.start()
- self.miner_fs.start_watching_progress()
self.tracker = trackertestutils.helpers.StoreHelper(
self.miner_fs.get_sparql_connection())
diff --git a/tests/functional-tests/meson.build b/tests/functional-tests/meson.build
index 8a996432c..5bb7d7c82 100644
--- a/tests/functional-tests/meson.build
+++ b/tests/functional-tests/meson.build
@@ -1,5 +1,11 @@
python = find_program('python3')
+if umockdev.found()
+ umockdev_wrapper = find_program('umockdev-wrapper')
+else
+ umockdev_wrapper = python
+endif
+
# Configure functional tests to run completely from source tree.
testconf = configuration_data()
@@ -7,7 +13,8 @@ config_json_full_path = meson.current_build_dir() / 'configuration.json'
dconf_profile_full_path = meson.current_source_dir() / 'trackertest'
tracker_extractors_dir = meson.current_build_dir() / '..' / '..' / 'src' / 'tracker-extract'
-testconf.set('TEST_DBUS_DAEMON_CONFIG_FILE', build_root / 'tests' / 'test-bus.conf')
+testconf.set('TEST_SESSION_BUS_CONFIG_FILE', build_root / 'tests' / 'test-session-bus.conf')
+testconf.set('TEST_SYSTEM_BUS_CONFIG_FILE', meson.current_source_dir() / '..' / 'test-system-bus.conf')
testconf.set('TEST_DCONF_PROFILE', dconf_profile_full_path)
testconf.set('TEST_DOMAIN_ONTOLOGY_RULE', meson.current_build_dir() / 'test-domain.rule')
testconf.set('TEST_EXTRACTOR_RULES_DIR', tracker_uninstalled_extract_rules_dir)
@@ -149,6 +156,10 @@ else
warning('No GStreamer h264 codec was detected. Some extractor tests will be disabled.')
endif
+umockdev_tests = [
+ 'miner-power',
+]
+
test_env = environment()
if get_option('tracker_core') == 'subproject'
@@ -184,3 +195,15 @@ foreach t: functional_tests
suite: ['functional'],
timeout: 120)
endforeach
+
+if umockdev.found()
+ # FIXME: these tests don't appear in the test runner output if umockdev
+ # wasn't found. We should really report them as skipped, but it's tricky.
+ foreach t: umockdev_tests
+ file = meson.current_source_dir() / '@0@.py'.format(t)
+ test(t, umockdev_wrapper,
+ args: [python.path(), file],
+ env: test_env,
+ suite: ['functional', 'umockdev'])
+ endforeach
+endif
diff --git a/tests/functional-tests/miner-power.py b/tests/functional-tests/miner-power.py
new file mode 100755
index 000000000..4220dff73
--- /dev/null
+++ b/tests/functional-tests/miner-power.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2020, Sam Thursfield (sam afuera me uk)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+"""
+Test that the miner responds to changes in power / battery status.
+"""
+
+import os
+import sys
+
+import configuration
+import fixtures
+import minerhelper
+import trackertestutils.devices
+
+
+testbed = None
+
+class MinerPowerTest(fixtures.TrackerMinerTest):
+ def setUp(self):
+ # We don't use setUp() from the base class because we need to start
+ # upowerd before the miner-fs.
+
+ extra_env = configuration.test_environment(self.workdir)
+ extra_env['LANG'] = 'en_GB.utf8'
+
+ # This sets the UMOCKDEV_DIR variable in the process environment.
+ #testbed = self.sandbox.get_umockdev_testbed()
+ self.battery = trackertestutils.devices.MockBattery(testbed)
+ self.battery.set_battery_power_normal_charge()
+
+ self.sandbox = trackertestutils.helpers.TrackerDBusSandbox(
+ dbus_daemon_config_file=configuration.TEST_DBUS_DAEMON_CONFIG_FILE, extra_env=extra_env)
+ self.sandbox.start()
+
+ try:
+ self.sandbox.set_config(self.config())
+
+ self.sandbox.start_upowerd(testbed)
+ self.miner_fs = minerhelper.MinerFsHelper(self.sandbox.get_connection())
+ self.miner_fs.start()
+ except Exception:
+ self.sandbox.stop()
+ raise
+
+ def test_miner_fs_pause_on_low_battery(self):
+ """The miner-fs should stop indexing if there's a low battery warning."""
+ minerhelper.await_status(self.miner_fs.miner_fs, "Idle")
+
+ with minerhelper.await_signal(self.miner_fs.miner_fs, "Paused"):
+ self.battery.set_battery_power_low_charge()
+ self.assertEqual(self.miner_fs.get_status(), "Paused")
+
+ with minerhelper.await_signal(self.miner_fs.miner_fs, "Resumed"):
+ self.battery.set_ac_power()
+ self.assertEqual(self.miner_fs.get_status(), "Idle")
+
+
+if __name__ == "__main__":
+ if not trackertestutils.devices.HAVE_UMOCKDEV:
+ # Return 'skipped' error code so `meson test` reports the test
+ # correctly.
+ sys.exit(77)
+
+ if not trackertestutils.devices.libumockdev_loaded():
+ raise RuntimeError("This test must be run inside umockdev-wrapper.")
+
+ # This sets a process-wide environment variable UMOCKDEV_DIR.
+ testbed = trackertestutils.devices.create_testbed()
+
+ fixtures.tracker_test_main()
diff --git a/tests/functional-tests/minerhelper.py b/tests/functional-tests/minerhelper.py
index 4aadd97da..01e874c07 100644
--- a/tests/functional-tests/minerhelper.py
+++ b/tests/functional-tests/minerhelper.py
@@ -20,11 +20,13 @@
import gi
gi.require_version('Tracker', '3.0')
-from gi.repository import Gio, GLib
+from gi.repository import Gio, GLib, GObject
from gi.repository import Tracker
+import contextlib
import logging
+import trackertestutils.dbusdaemon
import trackertestutils.mainloop
import configuration
@@ -33,10 +35,82 @@ import configuration
log = logging.getLogger(__name__)
-class WakeupCycleTimeoutException(RuntimeError):
+class AwaitTimeoutException(RuntimeError):
pass
+def await_status(miner_iface, target_status, timeout=configuration.DEFAULT_TIMEOUT):
+ log.info("Blocking until miner reports status of %s", target_status)
+ loop = trackertestutils.mainloop.MainLoop()
+
+ if miner_iface.GetStatus() == target_status:
+ log.info("Status is %s now", target_status)
+ return
+
+ def signal_cb(proxy, sender_name, signal_name, parameters):
+ if signal_name == 'Progress':
+ status, progress, remaining_time = parameters.unpack()
+ log.debug("Got status: %s", status)
+ if status == target_status:
+ loop.quit()
+
+ def timeout_cb():
+ log.info("Timeout fired after %s seconds", timeout)
+ raise AwaitTimeoutException(
+ f"Timeout awaiting miner status of '{target_status}'")
+
+ signal_id = miner_iface.connect('g-signal', signal_cb)
+ timeout_id = GLib.timeout_add_seconds(timeout, timeout_cb)
+
+ loop.run_checked()
+
+ GObject.signal_handler_disconnect(miner_iface, signal_id)
+ GLib.source_remove(timeout_id)
+
+
+class await_signal():
+ """Context manager to await a specific D-Bus signal.
+
+ Useful to wait for org.freedesktop.Tracker3.Miner signals like
+ Paused and Resumed.
+
+ """
+ def __init__(self, miner_iface, signal_name,
+ timeout=configuration.DEFAULT_TIMEOUT):
+ self.miner_iface = miner_iface
+ self.signal_name = signal_name
+ self.timeout = timeout
+
+ self.loop = trackertestutils.mainloop.MainLoop()
+
+ def __enter__(self):
+ log.info("Awaiting signal %s", self.signal_name)
+
+ def signal_cb(proxy, sender_name, signal_name, parameters):
+ if signal_name == self.signal_name:
+ log.debug("Received signal %s", signal_name)
+ self.loop.quit()
+
+ def timeout_cb():
+ log.info("Timeout fired after %s seconds", self.timeout)
+ raise AwaitTimeoutException(
+ f"Timeout awaiting signal '{self.signal_name}'")
+
+ self.signal_id = self.miner_iface.connect('g-signal', signal_cb)
+ self.timeout_id = GLib.timeout_add_seconds(self.timeout, timeout_cb)
+
+ def __exit__(self, etype, evalue, etraceback):
+ if etype is not None:
+ return False
+
+ self.loop.run_checked()
+
+ GLib.source_remove(self.timeout_id)
+ GObject.signal_handler_disconnect(self.miner_iface, self.signal_id)
+
+ return True
+
+
class MinerFsHelper ():
MINERFS_BUSNAME = "org.freedesktop.Tracker3.Miner.Files"
@@ -62,78 +136,17 @@ class MinerFsHelper ():
def start(self):
self.miner_fs.Start()
+ trackertestutils.dbusdaemon.await_bus_name(self.bus, self.MINERFS_BUSNAME)
def stop(self):
self.miner_fs.Stop()
+ def get_status(self):
+ return self.miner_fs.GetStatus()
+
def get_sparql_connection(self):
return Tracker.SparqlConnection.bus_new(
'org.freedesktop.Tracker3.Miner.Files', None, self.bus)
- def start_watching_progress(self):
- self._previous_status = None
- self._target_wakeup_count = None
- self._wakeup_count = 0
-
- def signal_handler(proxy, sender_name, signal_name, parameters):
- if signal_name == 'Progress':
- self._progress_cb(*parameters.unpack())
-
- self._progress_handler_id = self.miner_fs.connect('g-signal', signal_handler)
-
- def stop_watching_progress(self):
- if self._progress_handler_id != 0:
- self.miner_fs.disconnect(self._progress_handler_id)
-
- def _progress_cb(self, status, progress, remaining_time):
- if self._previous_status is None:
- self._previous_status = status
- if self._previous_status != 'Idle' and status == 'Idle':
- self._wakeup_count += 1
-
- if self._target_wakeup_count is not None and self._wakeup_count >= self._target_wakeup_count:
- self.loop.quit()
-
- def wakeup_count(self):
- """Return the number of wakeup-to-idle cycles the miner-fs completed."""
- return self._wakeup_count
-
- def await_wakeup_count(self, target_wakeup_count, timeout=configuration.DEFAULT_TIMEOUT):
- """Block until the miner has completed N wakeup-and-idle cycles.
-
- This function is for use by miner-fs tests that should trigger an
- operation in the miner, but which do not cause a new resource to be
- inserted. These tests can instead wait for the status to change from
- Idle to Processing... and then back to Idle.
-
- The miner may change its status any number of times, but you can use
- this function reliably as follows:
-
- wakeup_count = miner_fs.wakeup_count()
- # Trigger a miner-fs operation somehow ...
- miner_fs.await_wakeup_count(wakeup_count + 1)
- # The miner has probably finished processing the operation now.
-
- If the timeout is reached before enough wakeup cycles complete, an
- exception will be raised.
-
- """
-
- assert self._target_wakeup_count is None
-
- if self._wakeup_count >= target_wakeup_count:
- log.debug("miner-fs wakeup count is at %s (target is %s). No need to wait", self._wakeup_count,
target_wakeup_count)
- else:
- def _timeout_cb():
- raise WakeupCycleTimeoutException()
- timeout_id = GLib.timeout_add_seconds(timeout, _timeout_cb)
-
- log.debug("Waiting for miner-fs wakeup count of %s (currently %s)", target_wakeup_count,
self._wakeup_count)
- self._target_wakeup_count = target_wakeup_count
- self.loop.run_checked()
-
- self._target_wakeup_count = None
- GLib.source_remove(timeout_id)
-
def index_file(self, uri):
return self.index.IndexFile('(s)', uri)
diff --git a/tests/meson.build b/tests/meson.build
index b559274ca..b58e0e15a 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -10,8 +10,8 @@ endif
subdir('services')
test_bus_conf_file = configure_file(
- input: 'test-bus.conf.in',
- output: 'test-bus.conf',
+ input: 'test-session-bus.conf.in',
+ output: 'test-session-bus.conf',
configuration: conf)
if get_option('functional_tests')
diff --git a/tests/test-bus.conf.in b/tests/test-session-bus.conf.in
similarity index 100%
rename from tests/test-bus.conf.in
rename to tests/test-session-bus.conf.in
diff --git a/tests/test-system-bus.conf b/tests/test-system-bus.conf
new file mode 100644
index 000000000..04e797135
--- /dev/null
+++ b/tests/test-system-bus.conf
@@ -0,0 +1,20 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <!-- Our well-known bus type, don't change this -->
+ <type>system</type>
+
+ <listen>unix:tmpdir=./</listen>
+
+ <standard_system_servicedirs/>
+
+ <policy context="default">
+ <!-- Allow everything to be sent -->
+ <allow send_destination="*"/>
+ <!-- Allow everything to be received -->
+ <allow eavesdrop="true"/>
+ <!-- Allow anyone to own anything -->
+ <allow own="*"/>
+ </policy>
+
+</busconfig>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]