| From ce840b6b111e1e109e511f6833d6aa419e2b723a Mon Sep 17 00:00:00 2001 |
| From: Philip Withnall <philip@tecnocode.co.uk> |
| Date: Tue, 23 Jan 2024 11:16:52 +0000 |
| Subject: [PATCH] Merge branch '2887-memory-monitor-tests' into 'main' |
| |
| tests: Fix race condition in memory-monitor-dbus.test |
| |
| Closes #2887 |
| |
| See merge request GNOME/glib!3844 |
| |
| Hopefully these commits fix the occasional failures we've been seeing: |
| https://bugzilla.yoctoproject.org/show_bug.cgi?id=15362 |
| |
| Upstream-Status: Backport |
| Signed-off-by: Ross Burton <ross.burton@arm.com> |
| --- |
| gio/tests/memory-monitor-dbus.py.in | 64 +++++++++++++------- |
| gio/tests/memory-monitor-portal.py.in | 54 ++++++++++------- |
| gio/tests/power-profile-monitor-dbus.py.in | 35 ++++++----- |
| gio/tests/power-profile-monitor-portal.py.in | 34 ++++++----- |
| 4 files changed, 113 insertions(+), 74 deletions(-) |
| |
| diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in |
| index bf32918..7aae01e 100755 |
| --- a/gio/tests/memory-monitor-dbus.py.in |
| +++ b/gio/tests/memory-monitor-dbus.py.in |
| @@ -16,7 +16,6 @@ import sys |
| import subprocess |
| import fcntl |
| import os |
| -import time |
| |
| import taptestrunner |
| |
| @@ -57,53 +56,74 @@ try: |
| fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) |
| self.last_warning = -1 |
| self.dbusmock = dbus.Interface(self.obj_lmm, dbusmock.MOCK_IFACE) |
| + |
| + try: |
| + self.wait_for_bus_object('org.freedesktop.LowMemoryMonitor', |
| + '/org/freedesktop/LowMemoryMonitor', |
| + system_bus=True) |
| + except: |
| + raise |
| + |
| self.memory_monitor = Gio.MemoryMonitor.dup_default() |
| + assert("GMemoryMonitorDBus" in str(self.memory_monitor)) |
| self.memory_monitor.connect("low-memory-warning", self.memory_warning_cb) |
| self.mainloop = GLib.MainLoop() |
| self.main_context = self.mainloop.get_context() |
| |
| + # The LowMemoryMonitor API is stateless: it doesn’t expose any |
| + # properties, just a warning signal. Emit the signal in a loop until |
| + # the GMemoryMonitor instance has initialised and synchronised to |
| + # the right state. |
| + def emit_warning(level): |
| + self.dbusmock.EmitWarning(level) |
| + return GLib.SOURCE_CONTINUE |
| + |
| + idle_id = GLib.idle_add(emit_warning, 0) |
| + while self.last_warning != 0: |
| + self.main_context.iteration(True) |
| + GLib.source_remove(idle_id) |
| + |
| def tearDown(self): |
| self.p_mock.terminate() |
| self.p_mock.wait() |
| |
| - def assertEventually(self, condition, message=None, timeout=50): |
| + def assertEventually(self, condition, message=None, timeout=5): |
| '''Assert that condition function eventually returns True. |
| |
| - Timeout is in deciseconds, defaulting to 50 (5 seconds). message is |
| + Timeout is in seconds, defaulting to 5 seconds. message is |
| printed on failure. |
| ''' |
| - while timeout >= 0: |
| - context = GLib.MainContext.default() |
| - 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)) |
| + if not message: |
| + message = 'timed out waiting for ' + str(condition) |
| + |
| + def timed_out_cb(message): |
| + self.fail(message) |
| + return GLib.SOURCE_REMOVE |
| + |
| + timeout_source = GLib.timeout_source_new_seconds(timeout) |
| + timeout_source.set_callback(timed_out_cb, message) |
| + timeout_source.attach(self.main_context) |
| + |
| + while not condition(): |
| + self.main_context.iteration(True) |
| + |
| + timeout_source.destroy() |
| |
| def memory_warning_cb(self, monitor, level): |
| + print("Received memory warning signal, level", level) |
| self.last_warning = level |
| self.main_context.wakeup() |
| |
| def test_low_memory_warning_signal(self): |
| '''LowMemoryWarning signal''' |
| |
| - # Wait 2 seconds |
| - timeout = 2 |
| - while timeout > 0: |
| - time.sleep(0.5) |
| - timeout -= 0.5 |
| - self.main_context.iteration(False) |
| - |
| self.dbusmock.EmitWarning(100) |
| # Wait 2 seconds or until warning |
| - self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 20) |
| + self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 2) |
| |
| self.dbusmock.EmitWarning(255) |
| # Wait 2 seconds or until warning |
| - self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 20) |
| + self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 2) |
| |
| except ImportError as e: |
| @unittest.skip("Cannot import %s" % e.name) |
| diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in |
| index 748cee8..f570508 100755 |
| --- a/gio/tests/memory-monitor-portal.py.in |
| +++ b/gio/tests/memory-monitor-portal.py.in |
| @@ -16,7 +16,6 @@ import sys |
| import subprocess |
| import fcntl |
| import os |
| -import time |
| |
| import taptestrunner |
| |
| @@ -80,26 +79,44 @@ try: |
| self.mainloop = GLib.MainLoop() |
| self.main_context = self.mainloop.get_context() |
| |
| + # The LowMemoryMonitor API is stateless: it doesn’t expose any |
| + # properties, just a warning signal. Emit the signal in a loop until |
| + # the GMemoryMonitor instance has initialised and synchronised to |
| + # the right state. |
| + def emit_warning(level): |
| + self.dbusmock.EmitWarning(level) |
| + return GLib.SOURCE_CONTINUE |
| + |
| + idle_id = GLib.idle_add(self.emit_warning, 0) |
| + while self.last_warning != 0: |
| + self.main_context.iteration(True) |
| + GLib.source_remove(idle_id) |
| + |
| def tearDown(self): |
| self.p_mock.terminate() |
| self.p_mock.wait() |
| |
| - def assertEventually(self, condition, message=None, timeout=50): |
| + def assertEventually(self, condition, message=None, timeout=5): |
| '''Assert that condition function eventually returns True. |
| |
| - Timeout is in deciseconds, defaulting to 50 (5 seconds). message is |
| + Timeout is in seconds, defaulting to 5 seconds. message is |
| printed on failure. |
| ''' |
| - while timeout >= 0: |
| - context = GLib.MainContext.default() |
| - 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)) |
| + if not message: |
| + message = 'timed out waiting for ' + str(condition) |
| + |
| + def timed_out_cb(message): |
| + self.fail(message) |
| + return GLib.SOURCE_REMOVE |
| + |
| + timeout_source = GLib.timeout_source_new_seconds(timeout) |
| + timeout_source.set_callback(timed_out_cb, message) |
| + timeout_source.attach(self.main_context) |
| + |
| + while not condition(): |
| + self.main_context.iteration(True) |
| + |
| + timeout_source.destroy() |
| |
| def portal_memory_warning_cb(self, monitor, level): |
| self.last_warning = level |
| @@ -108,20 +125,13 @@ try: |
| def test_low_memory_warning_portal_signal(self): |
| '''LowMemoryWarning signal''' |
| |
| - # Wait 2 seconds |
| - timeout = 2 |
| - while timeout > 0: |
| - time.sleep(0.5) |
| - timeout -= 0.5 |
| - self.main_context.iteration(False) |
| - |
| self.dbusmock.EmitWarning(100) |
| # Wait 2 seconds or until warning |
| - self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 20) |
| + self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 2) |
| |
| self.dbusmock.EmitWarning(255) |
| # Wait 2 seconds or until warning |
| - self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 20) |
| + self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 2) |
| |
| except ImportError as e: |
| @unittest.skip("Cannot import %s" % e.name) |
| diff --git a/gio/tests/power-profile-monitor-dbus.py.in b/gio/tests/power-profile-monitor-dbus.py.in |
| index 06e594f..f955afc 100755 |
| --- a/gio/tests/power-profile-monitor-dbus.py.in |
| +++ b/gio/tests/power-profile-monitor-dbus.py.in |
| @@ -16,7 +16,6 @@ import sys |
| import subprocess |
| import fcntl |
| import os |
| -import time |
| |
| import taptestrunner |
| |
| @@ -58,6 +57,7 @@ try: |
| self.power_saver_enabled = False |
| self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) |
| self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() |
| + assert("GPowerProfileMonitorDBus" in str(self.power_profile_monitor)) |
| self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb) |
| self.mainloop = GLib.MainLoop() |
| self.main_context = self.mainloop.get_context() |
| @@ -66,22 +66,27 @@ try: |
| self.p_mock.terminate() |
| self.p_mock.wait() |
| |
| - def assertEventually(self, condition, message=None, timeout=50): |
| + def assertEventually(self, condition, message=None, timeout=5): |
| '''Assert that condition function eventually returns True. |
| |
| - Timeout is in deciseconds, defaulting to 50 (5 seconds). message is |
| + Timeout is in seconds, defaulting to 5 seconds. message is |
| printed on failure. |
| ''' |
| - while timeout >= 0: |
| - context = GLib.MainContext.default() |
| - 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)) |
| + if not message: |
| + message = 'timed out waiting for ' + str(condition) |
| + |
| + def timed_out_cb(message): |
| + self.fail(message) |
| + return GLib.SOURCE_REMOVE |
| + |
| + timeout_source = GLib.timeout_source_new_seconds(timeout) |
| + timeout_source.set_callback(timed_out_cb, message) |
| + timeout_source.attach(self.main_context) |
| + |
| + while not condition(): |
| + self.main_context.iteration(True) |
| + |
| + timeout_source.destroy() |
| |
| def power_saver_enabled_cb(self, spec, data): |
| self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() |
| @@ -92,10 +97,10 @@ try: |
| |
| self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) |
| self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) |
| - self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) |
| + self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 1) |
| |
| self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) |
| - self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) |
| + self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 1) |
| |
| except ImportError as e: |
| @unittest.skip("Cannot import %s" % e.name) |
| diff --git a/gio/tests/power-profile-monitor-portal.py.in b/gio/tests/power-profile-monitor-portal.py.in |
| index 09e9a45..ad2abf6 100755 |
| --- a/gio/tests/power-profile-monitor-portal.py.in |
| +++ b/gio/tests/power-profile-monitor-portal.py.in |
| @@ -16,7 +16,6 @@ import sys |
| import subprocess |
| import fcntl |
| import os |
| -import time |
| |
| import taptestrunner |
| |
| @@ -90,22 +89,27 @@ try: |
| self.p_mock.terminate() |
| self.p_mock.wait() |
| |
| - def assertEventually(self, condition, message=None, timeout=50): |
| + def assertEventually(self, condition, message=None, timeout=5): |
| '''Assert that condition function eventually returns True. |
| |
| - Timeout is in deciseconds, defaulting to 50 (5 seconds). message is |
| + Timeout is in seconds, defaulting to 5 seconds. message is |
| printed on failure. |
| ''' |
| - while timeout >= 0: |
| - context = GLib.MainContext.default() |
| - 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)) |
| + if not message: |
| + message = 'timed out waiting for ' + str(condition) |
| + |
| + def timed_out_cb(message): |
| + self.fail(message) |
| + return GLib.SOURCE_REMOVE |
| + |
| + timeout_source = GLib.timeout_source_new_seconds(timeout) |
| + timeout_source.set_callback(timed_out_cb, message) |
| + timeout_source.attach(self.main_context) |
| + |
| + while not condition(): |
| + self.main_context.iteration(True) |
| + |
| + timeout_source.destroy() |
| |
| def power_saver_enabled_cb(self, spec, data): |
| self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() |
| @@ -116,10 +120,10 @@ try: |
| |
| self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) |
| self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) |
| - self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) |
| + self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 1) |
| |
| self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) |
| - self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) |
| + self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 1) |
| |
| def test_power_profile_power_saver_enabled_portal_default(self): |
| '''power-saver-enabled property default value''' |