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
This commit is contained in:
Philip Withnall 2024-01-23 11:16:52 +00:00
commit fee9223e74
4 changed files with 113 additions and 74 deletions

View File

@ -16,7 +16,6 @@ import sys
import subprocess import subprocess
import fcntl import fcntl
import os import os
import time
import taptestrunner import taptestrunner
@ -57,53 +56,74 @@ try:
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.last_warning = -1 self.last_warning = -1
self.dbusmock = dbus.Interface(self.obj_lmm, dbusmock.MOCK_IFACE) 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() 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.memory_monitor.connect("low-memory-warning", self.memory_warning_cb)
self.mainloop = GLib.MainLoop() self.mainloop = GLib.MainLoop()
self.main_context = self.mainloop.get_context() self.main_context = self.mainloop.get_context()
# The LowMemoryMonitor API is stateless: it doesnt 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): def tearDown(self):
self.p_mock.terminate() self.p_mock.terminate()
self.p_mock.wait() 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. '''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. printed on failure.
''' '''
while timeout >= 0: if not message:
context = GLib.MainContext.default() message = 'timed out waiting for ' + str(condition)
while context.iteration(False):
pass def timed_out_cb(message):
if condition(): self.fail(message)
break return GLib.SOURCE_REMOVE
timeout -= 1
time.sleep(0.1) timeout_source = GLib.timeout_source_new_seconds(timeout)
else: timeout_source.set_callback(timed_out_cb, message)
self.fail(message or 'timed out waiting for ' + str(condition)) timeout_source.attach(self.main_context)
while not condition():
self.main_context.iteration(True)
timeout_source.destroy()
def memory_warning_cb(self, monitor, level): def memory_warning_cb(self, monitor, level):
print("Received memory warning signal, level", level)
self.last_warning = level self.last_warning = level
self.main_context.wakeup() self.main_context.wakeup()
def test_low_memory_warning_signal(self): def test_low_memory_warning_signal(self):
'''LowMemoryWarning signal''' '''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) self.dbusmock.EmitWarning(100)
# Wait 2 seconds or until warning # 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) self.dbusmock.EmitWarning(255)
# Wait 2 seconds or until warning # 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: except ImportError as e:
@unittest.skip("Cannot import %s" % e.name) @unittest.skip("Cannot import %s" % e.name)

View File

@ -16,7 +16,6 @@ import sys
import subprocess import subprocess
import fcntl import fcntl
import os import os
import time
import taptestrunner import taptestrunner
@ -80,26 +79,44 @@ try:
self.mainloop = GLib.MainLoop() self.mainloop = GLib.MainLoop()
self.main_context = self.mainloop.get_context() self.main_context = self.mainloop.get_context()
# The LowMemoryMonitor API is stateless: it doesnt 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): def tearDown(self):
self.p_mock.terminate() self.p_mock.terminate()
self.p_mock.wait() 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. '''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. printed on failure.
''' '''
while timeout >= 0: if not message:
context = GLib.MainContext.default() message = 'timed out waiting for ' + str(condition)
while context.iteration(False):
pass def timed_out_cb(message):
if condition(): self.fail(message)
break return GLib.SOURCE_REMOVE
timeout -= 1
time.sleep(0.1) timeout_source = GLib.timeout_source_new_seconds(timeout)
else: timeout_source.set_callback(timed_out_cb, message)
self.fail(message or 'timed out waiting for ' + str(condition)) 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): def portal_memory_warning_cb(self, monitor, level):
self.last_warning = level self.last_warning = level
@ -108,20 +125,13 @@ try:
def test_low_memory_warning_portal_signal(self): def test_low_memory_warning_portal_signal(self):
'''LowMemoryWarning signal''' '''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) self.dbusmock.EmitWarning(100)
# Wait 2 seconds or until warning # 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) self.dbusmock.EmitWarning(255)
# Wait 2 seconds or until warning # 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: except ImportError as e:
@unittest.skip("Cannot import %s" % e.name) @unittest.skip("Cannot import %s" % e.name)

View File

@ -16,7 +16,6 @@ import sys
import subprocess import subprocess
import fcntl import fcntl
import os import os
import time
import taptestrunner import taptestrunner
@ -58,6 +57,7 @@ try:
self.power_saver_enabled = False self.power_saver_enabled = False
self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE)
self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() 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.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb)
self.mainloop = GLib.MainLoop() self.mainloop = GLib.MainLoop()
self.main_context = self.mainloop.get_context() self.main_context = self.mainloop.get_context()
@ -66,22 +66,27 @@ try:
self.p_mock.terminate() self.p_mock.terminate()
self.p_mock.wait() 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. '''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. printed on failure.
''' '''
while timeout >= 0: if not message:
context = GLib.MainContext.default() message = 'timed out waiting for ' + str(condition)
while context.iteration(False):
pass def timed_out_cb(message):
if condition(): self.fail(message)
break return GLib.SOURCE_REMOVE
timeout -= 1
time.sleep(0.1) timeout_source = GLib.timeout_source_new_seconds(timeout)
else: timeout_source.set_callback(timed_out_cb, message)
self.fail(message or 'timed out waiting for ' + str(condition)) 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): def power_saver_enabled_cb(self, spec, data):
self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() 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.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.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.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: except ImportError as e:
@unittest.skip("Cannot import %s" % e.name) @unittest.skip("Cannot import %s" % e.name)

View File

@ -16,7 +16,6 @@ import sys
import subprocess import subprocess
import fcntl import fcntl
import os import os
import time
import taptestrunner import taptestrunner
@ -90,22 +89,27 @@ try:
self.p_mock.terminate() self.p_mock.terminate()
self.p_mock.wait() 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. '''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. printed on failure.
''' '''
while timeout >= 0: if not message:
context = GLib.MainContext.default() message = 'timed out waiting for ' + str(condition)
while context.iteration(False):
pass def timed_out_cb(message):
if condition(): self.fail(message)
break return GLib.SOURCE_REMOVE
timeout -= 1
time.sleep(0.1) timeout_source = GLib.timeout_source_new_seconds(timeout)
else: timeout_source.set_callback(timed_out_cb, message)
self.fail(message or 'timed out waiting for ' + str(condition)) 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): def power_saver_enabled_cb(self, spec, data):
self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() 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.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.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.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): def test_power_profile_power_saver_enabled_portal_default(self):
'''power-saver-enabled property default value''' '''power-saver-enabled property default value'''