python-dbusmock
python-dbusmock copied to clipboard
Add assertEventually helper
As used and implemented in upower.
As python-dbusmock uses the GLib mainloop in a number of places, it might be useful to add this helper to python-dbusmock.
That seems fine to me. I just really wonder how you use that in upower, I left a question there.
FTR, I'm on PTO until Aug 25 with no access to computers, so this will take a while.
I tried this patch:
diff --git dbusmock/testcase.py dbusmock/testcase.py
index 6fb81d6..9814b8e 100644
--- dbusmock/testcase.py
+++ dbusmock/testcase.py
@@ -21,6 +21,8 @@ from typing import Tuple, Dict, Any
import dbus
+from gi.repository import GLib
+
from dbusmock.mockobject import MOCK_IFACE, OBJECT_MANAGER_IFACE, load_module
@@ -254,3 +256,20 @@ class DBusTestCase(unittest.TestCase):
dbus_interface=MOCK_IFACE)
return (daemon, obj)
+
+ def assertEventually(self, condition, message=None, timeout=50):
+ '''Assert that condition function eventually returns True
+
+ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
+ printed on failure. GLib main loop is iterated while waiting.
+ '''
+ context = GLib.MainContext.default()
+ while timeout >= 0:
+ while context.iteration(False):
+ pass
+ if condition():
+ break
+ timeout -= 1
+ time.sleep(0.1)
+ else:
+ self.fail(message or 'timed out waiting for ' + str(condition))
diff --git tests/test_iio_sensors_proxy.py tests/test_iio_sensors_proxy.py
index 481ad58..2025720 100644
--- tests/test_iio_sensors_proxy.py
+++ tests/test_iio_sensors_proxy.py
@@ -62,9 +62,9 @@ class TestIIOSensorsProxyBase(dbusmock.DBusTestCase):
def set_internal_property(self, name, value):
return self.p_obj.SetInternalProperty(self.dbus_interface, name, value)
- def wait_for_properties_changed(self, max_wait=2000):
+ # max_wait is timeout in deciseconds
+ def wait_for_properties_changed(self, max_wait=20):
changed_properties = []
- timeout_id = 0
def on_properties_changed(interface, properties, _invalidated):
nonlocal changed_properties
@@ -72,23 +72,12 @@ class TestIIOSensorsProxyBase(dbusmock.DBusTestCase):
if interface == self.dbus_interface:
changed_properties = properties.keys()
- def on_timeout():
- nonlocal timeout_id
-
- timeout_id = 0
-
- loop = GLib.MainLoop()
- timeout_id = GLib.timeout_add(max_wait, on_timeout)
+ # loop = GLib.MainLoop()
match = self.p_obj.connect_to_signal('PropertiesChanged',
on_properties_changed,
dbus.PROPERTIES_IFACE)
- while not changed_properties and timeout_id != 0:
- loop.get_context().iteration(True)
-
- if timeout_id:
- GLib.source_remove(timeout_id)
-
+ self.assertEventually(lambda: changed_properties, timeout=max_wait)
match.remove()
return changed_properties
@@ -140,7 +129,7 @@ class TestIIOSensorsProxy(TestIIOSensorsProxyBase):
self.set_internal_property('HasAccelerometer', True)
self.assertTrue(self.get_property('HasAccelerometer'))
self.set_internal_property('AccelerometerOrientation', 'normal')
- self.assertFalse(self.wait_for_properties_changed(max_wait=500))
+ self.assertFalse(self.wait_for_properties_changed(max_wait=5))
self.assertEqual(self.get_property('AccelerometerOrientation'),
'normal')
@@ -189,7 +178,7 @@ class TestIIOSensorsProxy(TestIIOSensorsProxyBase):
self.set_internal_property('HasAmbientLight', True)
self.assertTrue(self.get_property('HasAmbientLight'))
self.set_internal_property('LightLevelUnit', 'vendor')
- self.assertFalse(self.wait_for_properties_changed(max_wait=500))
+ self.assertFalse(self.wait_for_properties_changed(max_wait=5))
self.assertEqual(self.get_property('LightLevelUnit'), 'vendor')
def test_proximity_none(self):
@@ -230,7 +219,7 @@ class TestIIOSensorsProxy(TestIIOSensorsProxyBase):
self.set_internal_property('HasProximity', True)
self.assertTrue(self.get_property('HasProximity'))
self.set_internal_property('ProximityNear', True)
- self.assertFalse(self.wait_for_properties_changed(max_wait=500))
+ self.assertFalse(self.wait_for_properties_changed(max_wait=5))
self.assertTrue(self.get_property('ProximityNear'))
@@ -277,7 +266,7 @@ class TestIIOSensorsProxyCompass(TestIIOSensorsProxyBase):
self.set_internal_property('HasCompass', True)
self.assertTrue(self.get_property('HasCompass'))
self.set_internal_property('CompassHeading', 85)
- self.assertFalse(self.wait_for_properties_changed(max_wait=500))
+ self.assertFalse(self.wait_for_properties_changed(max_wait=5))
self.assertEqual(self.get_property('CompassHeading'), 85)
but it doesn't work:
# PYTHONPATH=. python3 tests/test_iio_sensors_proxy.py TestIIOSensorsProxy.test_accelerometer_unclaimed_properties_changes
test_accelerometer_unclaimed_properties_changes (__main__.TestIIOSensorsProxy) ... FAIL
======================================================================
FAIL: test_accelerometer_unclaimed_properties_changes (__main__.TestIIOSensorsProxy)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/source/tests/test_iio_sensors_proxy.py", line 132, in test_accelerometer_unclaimed_properties_changes
self.assertFalse(self.wait_for_properties_changed(max_wait=5))
File "/source/tests/test_iio_sensors_proxy.py", line 80, in wait_for_properties_changed
self.assertEventually(lambda: changed_properties)
File "/source/dbusmock/testcase.py", line 275, in assertEventually
self.fail(message or 'timed out waiting for ' + str(condition))
AssertionError: timed out waiting for <function TestIIOSensorsProxyBase.wait_for_properties_changed.<locals>.<lambda> at 0x7f06099725f0>
It's also a wee bit weird, as this is adding GLib dependency/importing to the otherwise rather generic testcase.py (but dbusmock depends on GLib anyway, so I don't mind that much).
So this needs more debugging.