diff options
Diffstat (limited to 'plugins/power/test.py')
-rwxr-xr-x | plugins/power/test.py | 1397 |
1 files changed, 1397 insertions, 0 deletions
diff --git a/plugins/power/test.py b/plugins/power/test.py new file mode 100755 index 0000000..7bb190a --- /dev/null +++ b/plugins/power/test.py @@ -0,0 +1,1397 @@ +#!/usr/bin/python3 +'''GNOME settings daemon tests for power plugin.''' + +__author__ = 'Martin Pitt <martin.pitt@ubuntu.com>' +__copyright__ = '(C) 2013 Canonical Ltd.' +__license__ = 'GPL v2 or later' + +import unittest +import subprocess +import sys +import time +import math +import os +import os.path +import signal + +project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +builddir = os.environ.get('BUILDDIR', os.path.dirname(__file__)) + +sys.path.insert(0, os.path.join(project_root, 'tests')) +sys.path.insert(0, builddir) +import gsdtestcase +import gsdpowerconstants +import gsdpowerenums + +import dbus +from dbus.mainloop.glib import DBusGMainLoop + +DBusGMainLoop(set_as_default=True) + +import gi +gi.require_version('UPowerGlib', '1.0') +gi.require_version('UMockdev', '1.0') + +from gi.repository import Gio +from gi.repository import GLib +from gi.repository import UPowerGlib +from gi.repository import UMockdev + +class PowerPluginBase(gsdtestcase.GSDTestCase): + '''Test the power plugin''' + + COMMON_SUSPEND_METHODS=['Suspend', 'Hibernate', 'SuspendThenHibernate'] + + def setUp(self): + self.mock_external_monitor_file = os.path.join(self.workdir, 'GSD_MOCK_EXTERNAL_MONITOR') + os.environ['GSD_MOCK_EXTERNAL_MONITOR_FILE'] = self.mock_external_monitor_file + + self.check_logind_gnome_session() + self.start_logind() + self.daemon_death_expected = False + + + # Setup umockdev testbed + self.testbed = UMockdev.Testbed.new() + os.environ['UMOCKDEV_DIR'] = self.testbed.get_root_dir() + + # Create a mock backlight device + # Note that this function creates a different or even no backlight + # device based on the name of the test. + self.add_backlight() + + if 'HAVE_SYSFS_BACKLIGHT' in os.environ and os.environ['HAVE_SYSFS_BACKLIGHT'] == '1': + self.skip_sysfs_backlight = False + else: + self.skip_sysfs_backlight = True + + # start mock upowerd + (self.upowerd, self.obj_upower) = self.spawn_server_template( + 'upower', {'DaemonVersion': '0.99', 'OnBattery': True, 'LidIsClosed': False}, stdout=subprocess.PIPE) + gsdtestcase.set_nonblock(self.upowerd.stdout) + + # start mock gnome-shell screensaver + (self.screensaver, self.obj_screensaver) = self.spawn_server_template( + 'gnome_screensaver', stdout=subprocess.PIPE) + gsdtestcase.set_nonblock(self.screensaver.stdout) + + self.session_log_write = open(os.path.join(self.workdir, 'gnome-session.log'), 'wb', buffering=0) + self.session = subprocess.Popen(['gnome-session', '-f', + '-a', os.path.join(self.workdir, 'autostart'), + '--session=dummy', '--debug'], + stdout=self.session_log_write, + stderr=subprocess.STDOUT) + + # wait until the daemon is on the bus + try: + self.wait_for_bus_object('org.gnome.SessionManager', + '/org/gnome/SessionManager') + except: + # on failure, print log + with open(self.session_log_write.name) as f: + print('----- session log -----\n%s\n------' % f.read()) + raise + + self.session_log = open(self.session_log_write.name, 'rb', buffering=0) + + self.obj_session_mgr = self.session_bus_con.get_object( + 'org.gnome.SessionManager', '/org/gnome/SessionManager') + + self.start_mutter() + + # Set up the gnome-session presence + obj_session_presence = self.session_bus_con.get_object( + 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence') + self.obj_session_presence_props = dbus.Interface(obj_session_presence, dbus.PROPERTIES_IFACE) + + # ensure that our tests don't lock the screen when the screensaver + # gets active + self.settings_screensaver = Gio.Settings(schema_id='org.gnome.desktop.screensaver') + self.settings_screensaver['lock-enabled'] = False + + # Ensure we set up the external monitor state + self.set_has_external_monitor(False) + + self.settings_gsd_power = Gio.Settings(schema_id='org.gnome.settings-daemon.plugins.power') + + Gio.Settings.sync() + self.plugin_log_write = open(os.path.join(self.workdir, 'plugin_power.log'), 'wb', buffering=0) + # avoid painfully long delays of actions for tests + env = os.environ.copy() + # Disable PulseAudio output from libcanberra + env['CANBERRA_DRIVER'] = 'null' + + # Use dummy script as testing backlight helper + env['GSD_BACKLIGHT_HELPER'] = os.path.join (project_root, 'plugins', 'power', 'test-backlight-helper') + if 'POWER_LD_PRELOAD' in env: + if 'LD_PRELOAD' in env and env['LD_PRELOAD']: + env['LD_PRELOAD'] = ':'.join((env['POWER_LD_PRELOAD'], env['LD_PRELOAD'])) + else: + env['LD_PRELOAD'] = env['POWER_LD_PRELOAD'] + + # We need to redirect stdout to grab the debug messages. + # stderr is not needed by the testing infrastructure but is useful to + # see warnings and errors. + self.daemon = subprocess.Popen( + [os.path.join(builddir, 'gsd-power'), '--verbose'], + stdout=self.plugin_log_write, + env=env) + + # you can use this for reading the current daemon log in tests + self.plugin_log = open(self.plugin_log_write.name, 'rb', buffering=0) + + # wait until plugin is ready + timeout = 100 + while timeout > 0: + time.sleep(0.1) + timeout -= 1 + log = self.plugin_log.read() + if b'System inhibitor fd is' in log: + break + + # always start with zero idle time + self.reset_idle_timer() + + # flush notification log + self.p_notify.stdout.read() + + def tearDown(self): + + daemon_running = self.daemon.poll() == None + if daemon_running: + self.daemon.terminate() + self.daemon.wait() + self.plugin_log.close() + self.plugin_log_write.flush() + self.plugin_log_write.close() + + self.upowerd.terminate() + self.upowerd.wait() + self.screensaver.terminate() + self.screensaver.wait() + self.stop_session() + self.stop_mutter() + self.stop_logind() + + # reset all changed gsettings, so that tests are independent from each + # other + for schema in [self.settings_gsd_power, self.settings_session, self.settings_screensaver]: + for k in schema.list_keys(): + schema.reset(k) + Gio.Settings.sync() + + try: + os.unlink(self.mock_external_monitor_file) + except OSError: + pass + + del self.testbed + + # we check this at the end so that the other cleanup always happens + self.assertTrue(daemon_running or self.daemon_death_expected, 'daemon died during the test') + + def stop_session(self): + '''Stop GNOME session''' + + assert self.session + self.session.terminate() + self.session.wait() + + self.session_log_write.flush() + self.session_log_write.close() + self.session_log.close() + + def check_logind_gnome_session(self): + '''Check that gnome-session is built with logind support''' + + path = GLib.find_program_in_path ('gnome-session') + assert(path) + (success, data) = GLib.file_get_contents (path) + lines = data.split(b'\n') + new_path = None + for line in lines: + items = line.split() + if items and items[0] == b'exec': + new_path = items[1] + if not new_path: + self.fail("could not get gnome-session's real path from %s" % path) + path = new_path + ldd = subprocess.Popen(['ldd', path], stdout=subprocess.PIPE) + out = ldd.communicate()[0] + if not b'libsystemd.so.0' in out: + self.fail('gnome-session is not built with logind support') + + def get_status(self): + return self.obj_session_presence_props.Get('org.gnome.SessionManager.Presence', 'status') + + def backlight_defaults(self): + # Hack to modify the brightness defaults before starting gsd-power. + # The alternative would be to create two separate test files. + if 'no_backlight' in self.id(): + return None, None + elif 'legacy_brightness' in self.id(): + return 15, 15 + else: + return 100, 50 + + def add_backlight(self, _type="raw"): + max_brightness, brightness = self.backlight_defaults() + + if max_brightness is None: + self.backlight = None + return + + # Undo mangling done in GSD + if max_brightness >= 99: + max_brightness += 1 + brightness += 1 + + # This needs to be done before starting gsd-power! + self.backlight = self.testbed.add_device('backlight', 'mock_backlight', None, + ['type', _type, + 'max_brightness', str(max_brightness), + 'brightness', str(brightness)], + []) + + def get_brightness(self): + max_brightness = int(open(os.path.join(self.testbed.get_root_dir() + self.backlight, 'max_brightness')).read()) + + # self.backlight contains the leading slash, so os.path.join doesn't quite work + res = int(open(os.path.join(self.testbed.get_root_dir() + self.backlight, 'brightness')).read()) + # Undo mangling done in GSD + if max_brightness >= 99: + res -= 1 + return res + + def set_has_external_monitor(self, external): + if external: + val = b'1' + else: + val = b'0' + GLib.file_set_contents (self.mock_external_monitor_file, val) + + def set_composite_battery_discharging(self, icon='battery-good-symbolic'): + self.obj_upower.SetupDisplayDevice( + UPowerGlib.DeviceKind.BATTERY, + UPowerGlib.DeviceState.DISCHARGING, + 50., 50., 100., # 50%, charge 50 of 100 + 0.01, 600, 0, # Discharge rate 0.01 with 600 seconds remaining, 0 time to full + True, # present + icon, UPowerGlib.DeviceLevel.NONE + ) + + def set_composite_battery_critical(self, icon='battery-caution-symbolic'): + self.obj_upower.SetupDisplayDevice( + UPowerGlib.DeviceKind.BATTERY, + UPowerGlib.DeviceState.DISCHARGING, + 2., 2., 100., # 2%, charge 2 of 100 + 0.01, 60, 0, # Discharge rate 0.01 with 60 seconds remaining, 0 time to full + True, # present + icon, UPowerGlib.DeviceLevel.CRITICAL + ) + + def check_for_logout(self, timeout): + '''Check that logout is requested. + + Fail after the given timeout. + ''' + # check that it request logout + while timeout > 0: + time.sleep(1) + timeout -= 1 + # check that it requested logout + log = self.session_log.read() + if log is None: + continue + + if log and (b'GsmManager: requesting logout' in log): + break + else: + self.fail('timed out waiting for gnome-session logout call') + + def check_no_logout(self, seconds): + '''Check that no logout is requested in the given time''' + + # wait for specified time to ensure it didn't do anything + time.sleep(seconds) + # check that it did not logout + log = self.session_log.read() + if log: + self.assertFalse(b'GsmManager: requesting logout' in log, 'unexpected logout request') + + def check_for_suspend(self, timeout, methods=COMMON_SUSPEND_METHODS): + '''Check that one of the given suspend methods are requested. Default + methods are Suspend() or Hibernate() but also HibernateThenSuspend() + is valid. + + Fail after the given timeout. + ''' + + # Create a list of byte string needles to search for + needles = [' {} '.format(m).encode('ascii') for m in methods] + + suspended = False + + # check that it request suspend + while timeout > 0: + time.sleep(1) + timeout -= 1 + # check that it requested suspend + log = self.logind.stdout.read() + if log is None: + continue + + for n in needles: + if n in log: + suspended = True + break + + if suspended: + break + + if not suspended: + self.fail('timed out waiting for logind suspend call, methods: %s' % ', '.join(methods)) + + def check_for_lid_inhibited(self, timeout=0): + '''Check that the lid inhibitor has been added. + + Fail after the given timeout. + ''' + self.check_plugin_log('Adding lid switch system inhibitor', timeout, + 'Timed out waiting for lid inhibitor') + + def check_for_lid_uninhibited(self, timeout=0): + '''Check that the lid inhibitor has been dropped. + + Fail after the given timeout. + ''' + self.check_plugin_log('uninhibiting lid close', timeout, + 'Timed out waiting for lid uninhibition') + + def check_no_lid_uninhibited(self, timeout=0): + '''Check that the lid inhibitor has been dropped. + + Fail after the given timeout. + ''' + time.sleep(timeout) + # check that it requested uninhibition + log = self.plugin_log.read() + + if b'uninhibiting lid close' in log: + self.fail('lid uninhibit should not have happened') + + def check_no_suspend(self, seconds, methods=COMMON_SUSPEND_METHODS): + '''Check that no Suspend or Hibernate is requested in the given time''' + + # wait for specified time to ensure it didn't do anything + time.sleep(seconds) + # check that it did not suspend or hibernate + log = self.logind.stdout.read() + if log is None: + return + + for m in methods: + needle = ' {} '.format(m).encode('ascii') + + self.assertFalse(needle in log, 'unexpected %s request' % m) + + def check_suspend_no_hibernate(self, seconds): + '''Check that Suspend was requested and not Hibernate, in the given time''' + + # wait for specified time to ensure it didn't do anything + time.sleep(seconds) + # check that it did suspend and didn't hibernate + log = self.logind.stdout.read() + if log: + self.assertTrue(b' Suspend' in log, 'missing Suspend request') + self.assertFalse(b' Hibernate' in log, 'unexpected Hibernate request') + + def check_plugin_log(self, needle, timeout=0, failmsg=None): + '''Check that needle is found in the log within the given timeout. + Returns immediately when found. + + Fail after the given timeout. + ''' + if type(needle) == str: + needle = needle.encode('ascii') + # Fast path if the message was already logged + log = self.plugin_log.read() + if needle in log: + return + + while timeout > 0: + time.sleep(0.5) + timeout -= 0.5 + + # read new data (lines) from the log + log = self.plugin_log.read() + if needle in log: + break + else: + if failmsg is not None: + self.fail(failmsg) + else: + self.fail('timed out waiting for needle "%s"' % needle) + + def check_no_dim(self, seconds): + '''Check that mode is not set to dim in the given time''' + + # wait for specified time to ensure it didn't do anything + time.sleep(seconds) + # check that we don't dim + log = self.plugin_log.read() + if log: + self.assertFalse(b'Doing a state transition: dim' in log, 'unexpected dim request') + + def check_dim(self, timeout): + '''Check that mode is set to dim in the given time''' + + self.check_plugin_log('Doing a state transition: dim', timeout, + 'timed out waiting for dim') + + def check_undim(self, timeout): + '''Check that mode is set to normal in the given time''' + + self.check_plugin_log('Doing a state transition: normal', timeout, + 'timed out waiting for normal mode') + + def check_blank(self, timeout): + '''Check that blank is requested. + + Fail after the given timeout. + ''' + + self.check_plugin_log('TESTSUITE: Blanked screen', timeout, + 'timed out waiting for blank') + + def check_unblank(self, timeout): + '''Check that unblank is requested. + + Fail after the given timeout. + ''' + + self.check_plugin_log('TESTSUITE: Unblanked screen', timeout, + 'timed out waiting for unblank') + + def check_no_blank(self, seconds): + '''Check that no blank is requested in the given time''' + + # wait for specified time to ensure it didn't blank + time.sleep(seconds) + # check that it did not blank + log = self.plugin_log.read() + self.assertFalse(b'TESTSUITE: Blanked screen' in log, 'unexpected blank request') + + def check_no_unblank(self, seconds): + '''Check that no unblank is requested in the given time''' + + # wait for specified time to ensure it didn't unblank + time.sleep(seconds) + # check that it did not unblank + log = self.plugin_log.read() + self.assertFalse(b'TESTSUITE: Unblanked screen' in log, 'unexpected unblank request') + +class PowerPluginTest1(PowerPluginBase): + def test_screensaver(self): + # Note that the screensaver mock object + # doesn't know how to get out of being active, + # be it if the lock is disabled, or not. + + self.obj_screensaver.Lock() + # 0.3 second animation + time.sleep(1) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # blank is supposed to happen straight away + self.check_blank(2) + + # Wait a bit for the active watch to be registered through dbus, then + # fake user activity and check that the screen is unblanked. + time.sleep(0.5) + self.reset_idle_timer() + self.check_unblank(2) + + # Check for no blank before the normal blank timeout + self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # and check for blank after the blank timeout + self.check_blank(10) + + # Wait a bit for the active watch to be registered through dbus, then + # fake user activity and check that the screen is unblanked. + time.sleep(0.5) + self.reset_idle_timer() + self.check_unblank(2) + + # check no blank and then blank + self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + self.check_blank(10) + + def test_sleep_inactive_blank(self): + '''screensaver/blank interaction''' + + # create suspend inhibitor which should have no effect on the idle + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND), + dbus_interface='org.gnome.SessionManager') + + self.obj_screensaver.SetActive(True) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # blank is supposed to happen straight away + self.check_blank(2) + + # Wait a bit for the active watch to be registered through dbus, then + # fake user activity and check that the screen is unblanked. + time.sleep(0.5) + self.reset_idle_timer() + self.check_unblank(2) + if not self.skip_sysfs_backlight: + self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) + + # Check for no blank before the normal blank timeout + self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # and check for blank after the blank timeout + self.check_blank(10) + + # Drop inhibitor + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + +class PowerPluginTest2(PowerPluginBase): + def test_screensaver_no_unblank(self): + '''Ensure the screensaver is not unblanked for new inhibitors.''' + + # Lower idle delay a lot + self.settings_session['idle-delay'] = 1 + + # Bring down the screensaver + self.obj_screensaver.SetActive(True) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # Check that we blank + self.check_blank(2) + + # Create the different possible inhibitors + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_IDLE | gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND | gsdpowerenums.GSM_INHIBITOR_FLAG_LOGOUT), + dbus_interface='org.gnome.SessionManager') + + self.check_no_unblank(2) + + # Drop inhibitor + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + + self.check_no_unblank(2) + + def test_session_idle_delay(self): + '''verify that session idle delay works as expected when changed''' + + # Verify that idle is set after 5 seconds + self.settings_session['idle-delay'] = 5 + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + time.sleep(7) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) + + # Raise the idle delay, and see that we stop being idle + # and get idle again after the timeout + self.settings_session['idle-delay'] = 10 + # Resolve possible race condition, see also https://gitlab.gnome.org/GNOME/mutter/issues/113 + time.sleep(0.2) + self.reset_idle_timer() + time.sleep(5) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + time.sleep(10) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) + + # Lower the delay again, and see that we get idle as we should + self.settings_session['idle-delay'] = 5 + # Resolve possible race condition, see also https://gitlab.gnome.org/GNOME/mutter/issues/113 + time.sleep(0.2) + self.reset_idle_timer() + time.sleep(2) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + time.sleep(5) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) + + def test_idle_time_reset_on_resume(self): + '''Check that the IDLETIME is reset when resuming''' + + self.settings_screensaver['lock-enabled'] = False + + # Go idle + self.settings_session['idle-delay'] = 5 + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + time.sleep(7) + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) + + # Go to sleep + self.logind_obj.EmitSignal('', 'PrepareForSleep', 'b', [True], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # Wake up + self.logind_obj.EmitSignal('', 'PrepareForSleep', 'b', [False], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # And check we're not idle + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + +class PowerPluginTest3(PowerPluginBase): + def test_sleep_inactive_battery(self): + '''sleep-inactive-battery-timeout''' + + self.settings_session['idle-delay'] = 2 + self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' + + # wait for idle delay; should not yet suspend + self.check_no_suspend(2) + + # suspend should happen after inactive sleep timeout + 1 s notification + # delay + 1 s error margin + self.check_for_suspend(7) + + def _test_suspend_no_hibernate(self): + '''suspend-no-hibernate''' + + self.settings_session['idle-delay'] = 2 + self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 + # Hibernate isn't possible, so it should end up suspending + # FIXME + self.settings_gsd_power['critical-battery-action'] = 'hibernate' + + # wait for idle delay; should not yet hibernate + self.check_no_suspend(2) + + # suspend should happen after inactive sleep timeout + 1 s notification + # delay + 1 s error margin + self.check_suspend_no_hibernate(7) + + def test_sleep_inhibition(self): + '''Does not sleep under idle inhibition''' + + idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + + self.settings_session['idle-delay'] = idle_delay + self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' + + # create inhibitor + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_IDLE | gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND), + dbus_interface='org.gnome.SessionManager') + self.check_no_suspend(idle_delay + 2) + self.check_no_dim(0) + + # Check that we didn't go to idle either + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + +class PowerPluginTest4(PowerPluginBase): + def test_lock_on_lid_close(self): + '''Check that we do lock on lid closing, if the machine will not suspend''' + + self.settings_screensaver['lock-enabled'] = True + + # create inhibitor + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND), + dbus_interface='org.gnome.SessionManager') + + time.sleep (gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT) + + # Close the lid + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check that we've blanked + time.sleep(2) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + self.check_blank(2) + + # Drop the inhibit and see whether we suspend + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + # At this point logind should suspend for us + self.settings_screensaver['lock-enabled'] = False + + def test_blank_on_lid_close(self): + '''Check that we do blank on lid closing, if the machine will not suspend''' + + # create inhibitor + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND), + dbus_interface='org.gnome.SessionManager') + + time.sleep (gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT) + + # Close the lid + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check that we've blanked + self.check_blank(4) + + # Drop the inhibit and see whether we suspend + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + # At this point logind should suspend for us + + def test_unblank_on_lid_open(self): + '''Check that we do unblank on lid opening, if the machine will not suspend''' + + # create inhibitor + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND), + dbus_interface='org.gnome.SessionManager') + + time.sleep (gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT) + + # Close the lid + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check that we've blanked + self.check_blank(2) + + # Reopen the lid + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', False) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check for unblanking + self.check_unblank(2) + + # Drop the inhibit + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + +class PowerPluginTest5(PowerPluginBase): + def test_dim(self): + '''Check that we do go to dim''' + + # Wait and flush log + time.sleep (gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT + 1) + self.plugin_log.read() + + idle_delay = math.ceil(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + self.reset_idle_timer() + + self.settings_session['idle-delay'] = idle_delay + self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' + # This is an absolute percentage, and our brightness is 0..100 + dim_level = self.settings_gsd_power['idle-brightness']; + + # Check that we're not idle + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + + # Wait and check we're not idle, but dimmed + self.check_dim(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY + 1) + # Give time for the brightness to change + time.sleep(2) + if not self.skip_sysfs_backlight: + level = self.get_brightness(); + self.assertTrue(level == dim_level, 'incorrect dim brightness (%d != %d)' % (level, dim_level)) + + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + + # Bring down the screensaver + self.obj_screensaver.SetActive(True) + self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') + + # Check that we blank + self.check_blank(2) + + # Go to sleep + self.logind_obj.EmitSignal('', 'PrepareForSleep', 'b', [True], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # Wake up + self.logind_obj.EmitSignal('', 'PrepareForSleep', 'b', [False], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # And check that we have the pre-dim brightness + if not self.skip_sysfs_backlight: + self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) + + def test_lid_close_inhibition(self): + '''Check that we correctly inhibit suspend with an external monitor''' + + # Wait and flush log + time.sleep (gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT + 1) + self.plugin_log.read() + + # Add an external monitor + self.set_has_external_monitor(True) + self.check_for_lid_inhibited(1) + + # Check that we do not uninhibit with the external monitor attached + self.check_no_lid_uninhibited(gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT + 1) + + # Close the lid + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(0.5) + + # Unplug the external monitor + self.set_has_external_monitor(False) + + # Check that no action happens during the safety time minus 1 second + self.check_no_lid_uninhibited(gsdpowerconstants.LID_CLOSE_SAFETY_TIMEOUT - 1) + # Check that we're uninhibited after the safety time + self.check_for_lid_uninhibited(4) + +class PowerPluginTest6(PowerPluginBase): + def test_notify_critical_battery(self): + '''action on critical battery''' + + self.set_composite_battery_discharging() + + time.sleep(2) + + self.set_composite_battery_critical() + + # Check that it was picked up + self.check_plugin_log('EMIT: charge-critical', 2) + + # Wait a bit longer to ensure event has been fired + time.sleep(0.5) + # we should have gotten a notification now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* "battery-caution-symbolic" ".*battery critical.*"') + + def test_notify_critical_battery_on_start(self): + '''action on critical battery on startup''' + + self.set_composite_battery_critical() + + # Check that it was picked up + self.check_plugin_log('EMIT: charge-critical', 2) + + time.sleep(0.5) + + # we should have gotten a notification by now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* "battery-caution-symbolic" ".*battery critical.*"') + + def test_notify_device_battery(self): + '''critical power level notification for device batteries''' + + # Set internal battery to discharging + self.set_composite_battery_discharging() + + # Add a device battery + bat2_path = '/org/freedesktop/UPower/devices/' + 'mock_MOUSE_BAT1' + self.obj_upower.AddObject(bat2_path, + 'org.freedesktop.UPower.Device', + { + 'PowerSupply': dbus.Boolean(False, variant_level=1), + 'IsPresent': dbus.Boolean(True, variant_level=1), + 'Model': dbus.String('Bat1', variant_level=1), + 'Percentage': dbus.Double(40.0, variant_level=1), + 'TimeToEmpty': dbus.Int64(1600, variant_level=1), + 'EnergyFull': dbus.Double(100.0, variant_level=1), + 'Energy': dbus.Double(40.0, variant_level=1), + 'State': dbus.UInt32(UPowerGlib.DeviceState.DISCHARGING, variant_level=1), + 'Type': dbus.UInt32(UPowerGlib.DeviceKind.MOUSE, variant_level=1), + 'WarningLevel': dbus.UInt32(UPowerGlib.DeviceLevel.NONE, variant_level=1), + }, dbus.Array([], signature='(ssss)')) + + obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) + self.obj_upower.EmitSignal('', 'DeviceAdded', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # now change the mouse battery to critical charge + obj_bat2.Set('org.freedesktop.UPower.Device', 'TimeToEmpty', + dbus.Int64(30, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.Set('org.freedesktop.UPower.Device', 'Energy', + dbus.Double(0.5, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.Set('org.freedesktop.UPower.Device', 'WarningLevel', + dbus.UInt32(UPowerGlib.DeviceLevel.CRITICAL, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + self.obj_upower.EmitSignal('', 'DeviceChanged', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + + self.check_plugin_log('EMIT: charge-critical', 2) + time.sleep(0.5) + + # we should have gotten a notification by now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* ".*" ".*Wireless mouse .*low.* power.*\([0-9.]+%\).*"') + + def test_notify_device_spam(self): + '''no repeat notifications for device batteries''' + + # Set internal battery to discharging + self.set_composite_battery_discharging() + + # Add a device battery + bat2_path = '/org/freedesktop/UPower/devices/' + 'mock_MOUSE_BAT1' + self.obj_upower.AddObject(bat2_path, + 'org.freedesktop.UPower.Device', + { + 'PowerSupply': dbus.Boolean(False, variant_level=1), + 'IsPresent': dbus.Boolean(True, variant_level=1), + 'Model': dbus.String('Bat1', variant_level=1), + 'Serial': dbus.String('12345678', variant_level=1), + 'Percentage': dbus.Double(10.0, variant_level=1), + 'State': dbus.UInt32(UPowerGlib.DeviceState.DISCHARGING, variant_level=1), + 'Type': dbus.UInt32(UPowerGlib.DeviceKind.MOUSE, variant_level=1), + 'WarningLevel': dbus.UInt32(UPowerGlib.DeviceLevel.LOW, variant_level=1), + }, dbus.Array([], signature='(ssss)')) + + obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) + self.obj_upower.EmitSignal('', 'DeviceAdded', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + self.check_plugin_log('EMIT: charge-low', 2) + time.sleep(0.5) + + # we should have gotten a notification by now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* ".*" ".*Wireless mouse .*low.* power.*\([0-9.]+%\).*"') + + # Disconnect mouse + self.obj_upower.RemoveObject(bat2_path) + time.sleep(0.5) + + # Reconnect mouse + self.obj_upower.AddObject(bat2_path, + 'org.freedesktop.UPower.Device', + { + 'PowerSupply': dbus.Boolean(False, variant_level=1), + 'IsPresent': dbus.Boolean(True, variant_level=1), + 'Model': dbus.String('Bat1', variant_level=1), + 'Serial': dbus.String('12345678', variant_level=1), + 'Percentage': dbus.Double(10.0, variant_level=1), + 'State': dbus.UInt32(UPowerGlib.DeviceState.DISCHARGING, variant_level=1), + 'Type': dbus.UInt32(UPowerGlib.DeviceKind.MOUSE, variant_level=1), + 'WarningLevel': dbus.UInt32(UPowerGlib.DeviceLevel.LOW, variant_level=1), + }, dbus.Array([], signature='(ssss)')) + + obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) + self.obj_upower.EmitSignal('', 'DeviceAdded', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # we shouldn't have gotten a notification by now + notify_log = self.p_notify.stdout.read() + self.assertIsNone(notify_log) + + # Disconnect mouse + self.obj_upower.RemoveObject(bat2_path) + time.sleep(0.5) + + # Reconnect mouse with critical battery level + self.obj_upower.AddObject(bat2_path, + 'org.freedesktop.UPower.Device', + { + 'PowerSupply': dbus.Boolean(False, variant_level=1), + 'IsPresent': dbus.Boolean(True, variant_level=1), + 'Model': dbus.String('Bat1', variant_level=1), + 'Serial': dbus.String('12345678', variant_level=1), + 'Percentage': dbus.Double(5.0, variant_level=1), + 'State': dbus.UInt32(UPowerGlib.DeviceState.DISCHARGING, variant_level=1), + 'Type': dbus.UInt32(UPowerGlib.DeviceKind.MOUSE, variant_level=1), + 'WarningLevel': dbus.UInt32(UPowerGlib.DeviceLevel.CRITICAL, variant_level=1), + }, dbus.Array([], signature='(ssss)')) + + obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) + self.obj_upower.EmitSignal('', 'DeviceAdded', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # Verify new warning + self.check_plugin_log('EMIT: charge-critical', 2) + time.sleep(0.5) + + # we should have gotten a notification by now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* ".*" ".*Wireless mouse .*very low.* power.*\([0-9.]+%\).*"') + + def test_notify_device_battery_coarse_level(self): + '''critical power level notification for device batteries with coarse level''' + + # Set internal battery to discharging + self.set_composite_battery_discharging() + + # Add a device battery + bat2_path = '/org/freedesktop/UPower/devices/' + 'mock_MOUSE_BAT1' + self.obj_upower.AddObject(bat2_path, + 'org.freedesktop.UPower.Device', + { + 'PowerSupply': dbus.Boolean(False, variant_level=1), + 'IsPresent': dbus.Boolean(True, variant_level=1), + 'Model': dbus.String('Bat1', variant_level=1), + 'Percentage': dbus.Double(40.0, variant_level=1), + 'BatteryLevel': dbus.UInt32(UPowerGlib.DeviceLevel.LOW, variant_level=1), + 'TimeToEmpty': dbus.Int64(1600, variant_level=1), + 'EnergyFull': dbus.Double(100.0, variant_level=1), + 'Energy': dbus.Double(40.0, variant_level=1), + 'State': dbus.UInt32(UPowerGlib.DeviceState.DISCHARGING, variant_level=1), + 'Type': dbus.UInt32(UPowerGlib.DeviceKind.MOUSE, variant_level=1), + 'WarningLevel': dbus.UInt32(UPowerGlib.DeviceLevel.NONE, variant_level=1), + }, dbus.Array([], signature='(ssss)')) + + obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) + self.obj_upower.EmitSignal('', 'DeviceAdded', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + + # now change the mouse battery to critical charge + obj_bat2.Set('org.freedesktop.UPower.Device', 'TimeToEmpty', + dbus.Int64(30, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.Set('org.freedesktop.UPower.Device', 'Energy', + dbus.Double(0.5, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.Set('org.freedesktop.UPower.Device', 'WarningLevel', + dbus.UInt32(UPowerGlib.DeviceLevel.CRITICAL, variant_level=1), + dbus_interface=dbus.PROPERTIES_IFACE) + obj_bat2.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + self.obj_upower.EmitSignal('', 'DeviceChanged', 'o', [bat2_path], + dbus_interface='org.freedesktop.DBus.Mock') + + self.check_plugin_log('EMIT: charge-critical', 2) + time.sleep(0.5) + + # we should have gotten a notification by now + notify_log = self.p_notify.stdout.read() + + # verify notification + self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" .* ".*" ".*Wireless mouse .*low.* power.*"') + self.assertNotRegex(notify_log, b'[0-9.]+ Notify "Power" .* ".*" ".*\([0-9.]+%\).*"') + + def test_forced_logout(self): + '''Test forced logout''' + + self.daemon_death_expected = True + idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + + self.settings_session['idle-delay'] = idle_delay + self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'logout' + + self.check_for_logout(idle_delay + 2) + + # The notification should have been received before the logout, but it's saved anyway + notify_log = self.p_notify.stdout.read() + self.assertTrue(b'You will soon log out because of inactivity.' in notify_log) + + def test_forced_logout_inhibition(self): + '''Test we don't force logout when inhibited''' + + idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + + self.settings_session['idle-delay'] = idle_delay + self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'logout' + + # create suspend inhibitor which should stop us logging out + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_LOGOUT), + dbus_interface='org.gnome.SessionManager') + + self.check_no_logout(idle_delay + 3) + + # Drop inhibitor + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + +class PowerPluginTest7(PowerPluginBase): + def test_check_missing_kbd_brightness(self): + ''' https://bugzilla.gnome.org/show_bug.cgi?id=793512 ''' + + obj_gsd_power_kbd = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_kbd_props = dbus.Interface(obj_gsd_power_kbd, dbus.PROPERTIES_IFACE) + + # Will return -1 if gsd-power crashed, and an exception if the code caught the problem + with self.assertRaises(dbus.DBusException) as exc: + kbd_brightness = obj_gsd_power_kbd_props.Get('org.gnome.SettingsDaemon.Power.Keyboard', 'Brightness') + + # We should not have arrived here, if we did then the test failed, let's print this to help debugging + print('Got keyboard brightness: {}'.format(kbd_brightness)) + + self.assertEqual(exc.exception.get_dbus_message(), 'Failed to get property Brightness on interface org.gnome.SettingsDaemon.Power.Keyboard') + + def test_inhibitor_idletime(self): + ''' https://bugzilla.gnome.org/show_bug.cgi?id=705942 ''' + + idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + + self.settings_session['idle-delay'] = idle_delay + self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' + + # create inhibitor + inhibit_id = self.obj_session_mgr.Inhibit( + 'testsuite', dbus.UInt32(0), 'for testing', + dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_IDLE), + dbus_interface='org.gnome.SessionManager') + self.check_no_suspend(idle_delay + 2) + self.check_no_dim(0) + + # Check that we didn't go to idle either + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + + self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id), + dbus_interface='org.gnome.SessionManager') + + self.check_no_suspend(2) + self.check_no_dim(0) + + time.sleep(5) + + self.check_suspend_no_hibernate(7) + + def disabled_test_unindle_on_ac_plug(self): + idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) + self.settings_session['idle-delay'] = idle_delay + + # Wait for idle + self.check_dim(idle_delay + 2) + + # Plug in the AC + self.obj_upower.Set('org.freedesktop.UPower', 'OnBattery', False) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check that we undim + self.check_undim(gsdpowerconstants.POWER_UP_TIME_ON_AC / 2) + + # And wait a little more to see us dim again + self.check_dim(idle_delay + 2) + + # Unplug the AC + self.obj_upower.Set('org.freedesktop.UPower', 'OnBattery', True) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + + # Check that we undim + self.check_undim(gsdpowerconstants.POWER_UP_TIME_ON_AC / 2) + + # And wait a little more to see us dim again + self.check_dim(idle_delay + 2) + +class PowerPluginTest8(PowerPluginBase): + def test_brightness_stepping(self): + '''Check that stepping the backlight works as expected''' + + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_screen_iface = dbus.Interface(obj_gsd_power, 'org.gnome.SettingsDaemon.Power.Screen') + + # Each of the step calls will only return when the value was written + start = time.time() + # We start at 50% and step by 5% each time + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 55) + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 60) + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 65) + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 70) + stop = time.time() + # This needs to take more than 0.8 seconds as each write is delayed by + # 0.2 seconds by the test backlight helper + self.assertGreater(stop - start, 0.8) + + # Now, the same thing should work fine if we step multiple times, + # even if we are so quick that compression will happen. + # Use a list to keep rack of replies (as integer is immutable and would + # not be modified in the outer scope) + replies = [0] + + def handle_reply(*args): + replies[0] += 1 + + def last_reply(*args): + replies[0] += 1 + loop.quit() + + def error_handler(*args): + loop.quit() + + start = time.time() + obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) + obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) + obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) + obj_gsd_power_screen_iface.StepDown(reply_handler=last_reply, error_handler=error_handler) + loop = GLib.MainLoop() + loop.run() + stop = time.time() + + # The calls need to be returned in order. As we got the last reply, all + # others must have been received too. + self.assertEqual(replies[0], 4) + # Four steps down, so back at 50% + self.assertEqual(self.get_brightness(), 50) + # And compression must have happened, so it should take less than 0.8s + self.assertLess(stop - start, 0.8) + + def test_brightness_compression(self): + '''Check that compression also happens when setting the property''' + + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + # Now test that the compression works correctly. + # NOTE: Relies on the implementation detail, that the property setter + # returns immediately rather than waiting for the brightness to + # be updated. + # Should this ever be fixed, then this will need to be changed to use + # async dbus calls similar to the stepping code + + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) + + # Quickly ramp the brightness up + for brightness in range(70, 91): + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', brightness) + + # The brightness of 80 should be in effect after slightly more than + # 0.4 seconds. If compression does not work as expected, this would take + # more than 5 seconds for the 20 steps. + time.sleep(2.0) + self.assertEqual(self.get_brightness(), 90) + + def test_brightness_uevent(self): + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) + + brightness = obj_gsd_power_prop_iface.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') + self.assertEqual(50, brightness) + + # Check that the brightness is updated if it was changed through some + # other mechanism (e.g. firmware). + # Set to 80+1 because of the GSD offset (see add_backlight). + self.testbed.set_attribute(self.backlight, 'brightness', '81') + self.testbed.uevent(self.backlight, 'change') + + self.check_plugin_log('GsdBacklight: Got uevent', 1, 'gsd-power did not process uevent') + time.sleep(0.2) + + brightness = obj_gsd_power_prop_iface.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') + self.assertEqual(80, brightness) + + def test_brightness_step(self): + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + # We cannot use check_plugin_log here because the startup check already + # read the relevant message. + log = open(self.plugin_log_write.name, 'rb').read() + self.assertIn(b'Step size for backlight is 5.', log) + + def test_legacy_brightness_step(self): + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + # We cannot use check_plugin_log here because the startup check already + # read the relevant message. + log = open(self.plugin_log_write.name, 'rb').read() + self.assertIn(b'Step size for backlight is 1.', log) + + def test_legacy_brightness_rounding(self): + if self.skip_sysfs_backlight: + self.skipTest("sysfs backlight support required for test") + + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) + + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 0) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 0) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 10) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 2) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 20) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 3) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 25) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 4) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 49) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 7) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 50) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 8) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 56) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 8) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 57) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 9) + obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 98) + time.sleep(0.4) + self.assertEqual(self.get_brightness(), 15) + + def test_no_backlight(self): + '''Check that backlight brightness DBus api without a backlight''' + + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_props = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) + obj_gsd_power_screen = dbus.Interface(obj_gsd_power, 'org.gnome.SettingsDaemon.Power.Screen') + + # We expect -1 to be returned + brightness = obj_gsd_power_props.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') + self.assertEqual(brightness, -1) + + # Trying to set the brightness + with self.assertRaises(dbus.DBusException) as exc: + obj_gsd_power_props.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 1) + + self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') + + with self.assertRaises(dbus.DBusException) as exc: + obj_gsd_power_screen.StepUp() + + self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') + + with self.assertRaises(dbus.DBusException) as exc: + obj_gsd_power_screen.StepDown() + + self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') + +# avoid writing to stderr +unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) |