[neon/backports-jammy/power-profiles-daemon/Neon/unstable] /: 0.10.1-1 (patches unapplied)
git-ubuntu importer
null at kde.org
Tue Sep 24 23:21:42 BST 2024
Git commit 6a5c5f03481ec3e41c0d98b40eb11068b0370626 by git-ubuntu importer, on behalf of Sebastien Bacher.
Committed on 08/11/2021 at 22:37.
Pushed by carlosdem into branch 'Neon/unstable'.
0.10.1-1 (patches unapplied)
Imported using git-ubuntu import.
A +35 -0 .gitlab-ci.yml
M +33 -0 NEWS
M +46 -15 README.md
M +15 -0 data/meson.build
A +31 -0 data/net.hadess.PowerProfiles.policy
M +3 -1 data/power-profiles-daemon.service.in
M +10 -0 debian/changelog
M +2 -0 debian/control
A +29 -0 debian/patches/build_older_polkit.patch
M +1 -0 debian/patches/series
M +1 -0 docs/meson.build
M +9 -6 meson.build
M +4 -0 meson_options.txt
M +9 -2 src/meson.build
M +72 -8 src/net.hadess.PowerProfiles.xml
M +406 -39 src/power-profiles-daemon.c
M +116 -5 src/powerprofilesctl.in
M +17 -15 src/ppd-driver-fake.c
M +2 -3 src/ppd-driver-intel-pstate.c
M +13 -17 src/ppd-driver-platform-profile.c
M +33 -47 src/ppd-driver.c
M +12 -13 src/ppd-driver.h
R +305 -52 tests/integration-test.py [from: tests/integration-test - 058% similarity]
M +14 -6 tests/meson.build
A +46 -0 tests/unittest_inspector.py
https://invent.kde.org/neon/backports-jammy/power-profiles-daemon/-/commit/6a5c5f03481ec3e41c0d98b40eb11068b0370626
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..592a89d
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,35 @@
+image: fedora:rawhide
+
+variables:
+ DEPENDENCIES: gcc
+ gtk-doc
+ pkgconfig(udev)
+ pkgconfig(systemd)
+ pkgconfig(gio-2.0)
+ pkgconfig(gudev-1.0)
+ pkgconfig(upower-glib)
+ pkgconfig(polkit-gobject-1)
+ systemd
+ meson
+ git
+ python3-gobject
+ python3-dbusmock
+ python3-pylint
+ umockdev
+
+build_stable:
+ before_script:
+ - dnf upgrade -y --nogpgcheck fedora-release fedora-repos*
+ - dnf update -y && dnf install -y $DEPENDENCIES
+ script:
+ - meson -Dgtk_doc=true -Dpylint=true _build
+ - ninja -v -C _build
+ - ninja -v -C _build install
+ - ninja -v -C _build uninstall
+ - ninja -v -C _build dist
+ - meson test -C _build
+ artifacts:
+ when: always
+ paths:
+ - _build/meson-logs/*.txt
+ - _build/meson-dist/*
diff --git a/NEWS b/NEWS
index bc8a646..224ebbc 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,36 @@
+0.10.1
+------
+
+This release fixes a bug in the authorisation codepath added in 0.10.0, where holding
+a profile could still succeed despite having sent a denial to the calling process.
+
+This release also makes the pylint test optional. It should not be used unless the goal
+is to contribute patches to assuage it.
+
+0.10.0
+------
+
+This release adds authorisation checks for the profile holds and profile switching
+features of the backend daemon, through polkit. It is recommended that all
+distributions upgrade to this version as soon as possible.
+
+This release also adds support for the "quiet" kernel platform profile used
+in some systems.
+
+0.9.0
+-----
+
+This release adds support for "holding" a power profile while running a task
+or application, making it possible to switch to a performance profile during
+a compilation, or to a power-saver profile when low on battery, reverting to
+the original profile when done.
+
+This release also removes the "inhibited" property for the performance profile,
+which made it impossible to switch to that profile, and replaces it with the
+"degraded" property which lists why performance is degraded.
+
+Finally, the last used profile is now remembered across reboots.
+
0.8.1
-----
diff --git a/README.md b/README.md
index 768a744..99e102e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Installation
$ meson _build -Dprefix=/usr
$ ninja -v -C _build install
```
-It requires libgudev and systemd.
+It requires libgudev, systemd and polkit-gobject.
Introduction
------------
@@ -30,38 +30,47 @@ they are also expected to adjust the behaviour of the desktop depending on the m
such as turning the screen off after inaction more aggressively when in power-saver
mode.
-Note that power-profiles-daemon does not save the currently active profile across
-system restarts and will always start with the "balanced" profile selected.
-
Debugging
---------
You can now check which mode is in use, and which ones are available by running:
-```
-gdbus introspect --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles
+
+```sh
+powerprofilesctl
```
You can change the selected profile by running (change `power-saver` for the
chosen profile):
+
+```sh
+powerprofilesctl set power-saver
+```
+
+You can check the current configuration which will be restored on
+reboot in `/var/lib/power-profiles-daemon/state.ini`.
+
+Those commands are also available through the D-Bus interface:
+
```
+gdbus introspect --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles
gdbus call --system --dest net.hadess.PowerProfiles --object-path /net/hadess/PowerProfiles --method org.freedesktop.DBus.Properties.Set 'net.hadess.PowerProfiles' 'ActiveProfile' "<'power-saver'>"
```
-If that doesn't work, please file an issue, make sure any running power-profiles-daemon
-has been stopped:
-`systemctl stop power-profiles-daemon.service`
-and attach the output of:
-`G_MESSAGES_DEBUG=all /usr/libexec/power-profiles-daemon`
-running as ```root```.
+If that doesn't work, please file an issue, attach the output of:
+
+```sh
+sudo G_MESSAGES_DEBUG=all /usr/libexec/power-profiles-daemon -r -v
+```
Testing
-------
-If you don't have hardware that can support the performance mode, you can
-manually run the `power-profiles-daemon` binary as `root` with the environment
+If you don't have hardware that can support the performance mode, or the degraded mode
+you can manually run the `power-profiles-daemon` binary as `root` with the environment
variable `POWER_PROFILE_DAEMON_FAKE_DRIVER` set to 1. For example:
+
```sh
-$ sudo POWER_PROFILE_DAEMON_FAKE_DRIVER=1 /usr/libexec/power-profiles-daemon
+sudo POWER_PROFILE_DAEMON_FAKE_DRIVER=1 /usr/libexec/power-profiles-daemon -r -v
```
References
@@ -152,3 +161,25 @@ addition to the "3 profiles" selection:
A lot of those power-saving tricks could be analysed and used, but we
obviously can't rely on "source available" software for our free desktops.
+
+### [system76-power](https://github.com/pop-os/system76-power)
+
+Very similar project to power-profiles-daemon but goes much more into the weeds
+in terms of power-saving/performance implementation.
+
+It has a D-Bus API for choosing different power profiles, and applies a number
+of settings based on the profile selected. Most of the interesting settings are
+already upstreamed ([SATA power tweaks](https://hansdegoede.livejournal.com/18412.html)),
+should be upstreamed to the vanilla kernel if possible (PCI power-savings), or
+are things we already implement (Intel P-State).
+
+It could without a doubt have been used as a base for power-profiles-daemon if it
+was more of an upstream project instead of a PopOS!/System76 project.
+
+### [asusctl](https://gitlab.com/asus-linux/asusctl/)
+
+It provides an interface to a number of ASUS-specific features which isn't directly
+relevant to power-profiles-daemon like handling keyboard LED settings, or setting
+battery charge limits. The functionality that was relevant got moved to the asus-wmi
+kernel driver during the 5.14 kernel development cycle, where power-profiles-daemon
+can consume it. The 2 daemons are now complementary.
diff --git a/data/meson.build b/data/meson.build
index 8904325..3d1b466 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -19,3 +19,18 @@ install_data(
'net.hadess.PowerProfiles.service',
install_dir: dbusservicedir
)
+
+polkit_policy = 'net.hadess.PowerProfiles.policy'
+if xmllint.found()
+ test(polkit_policy,
+ xmllint,
+ args: [
+ '--noout',
+ meson.source_root() / 'data' / polkit_policy,
+ ])
+endif
+
+install_data(
+ polkit_policy,
+ install_dir: polkit_policy_directory,
+)
diff --git a/data/net.hadess.PowerProfiles.policy b/data/net.hadess.PowerProfiles.policy
new file mode 100644
index 0000000..a332b42
--- /dev/null
+++ b/data/net.hadess.PowerProfiles.policy
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+ <vendor>power-profiles-daemon</vendor>
+ <vendor_url>https://gitlab.freedesktop.org/hadess/power-profiles-daemon</vendor_url>
+
+ <action id="net.hadess.PowerProfiles.switch-profile">
+ <description>Switch Power Profile</description>
+ <message>Privileges are required to switch power profiles.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="net.hadess.PowerProfiles.hold-profile">
+ <description>Hold Power Profile</description>
+ <message>Privileges are required to hold power profiles.</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff --git a/data/power-profiles-daemon.service.in b/data/power-profiles-daemon.service.in
index 8fe56ce..dcd3503 100644
--- a/data/power-profiles-daemon.service.in
+++ b/data/power-profiles-daemon.service.in
@@ -1,6 +1,6 @@
[Unit]
Description=Power Profiles daemon
-Conflicts=tuned.service tlp.service auto-cpufreq.service
+Conflicts=tuned.service tlp.service auto-cpufreq.service system76-power.service
Before=multi-user.target display-manager.target
[Service]
@@ -8,6 +8,8 @@ Type=dbus
BusName=net.hadess.PowerProfiles
ExecStart=@libexecdir@/power-profiles-daemon
Restart=on-failure
+# This always corresponds to /var/lib/power-profiles-daemon
+StateDirectory=power-profiles-daemon
#Uncomment this to enable debug
#Environment="G_MESSAGES_DEBUG=all"
diff --git a/debian/changelog b/debian/changelog
index 3155e44..3bae784 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+power-profiles-daemon (0.10.1-1) unstable; urgency=medium
+
+ * New upstream version
+ * debian/control:
+ - Build-Depends on libpolkit-gobject-1-dev and libxml2-utils
+ * debian/patches/build_older_polkit.patch:
+ - fix the build with older polkit versions
+
+ -- Sebastien Bacher <seb128 at ubuntu.com> Mon, 08 Nov 2021 16:31:07 +0100
+
power-profiles-daemon (0.8.1-1) experimental; urgency=medium
* New upstream version
diff --git a/debian/control b/debian/control
index 757ab1c..b90929d 100644
--- a/debian/control
+++ b/debian/control
@@ -6,9 +6,11 @@ Uploaders: Sebastien Bacher <seb128 at ubuntu.com>
Build-Depends: debhelper-compat (= 13),
libglib2.0-dev,
libgudev-1.0-dev,
+ libpolkit-gobject-1-dev,
libupower-glib-dev,
libudev-dev,
libumockdev-dev,
+ libxml2-utils,
meson,
python3-dbus <!nocheck>,
python3-dbusmock <!nocheck>,
diff --git a/debian/patches/build_older_polkit.patch b/debian/patches/build_older_polkit.patch
new file mode 100644
index 0000000..6cd50f2
--- /dev/null
+++ b/debian/patches/build_older_polkit.patch
@@ -0,0 +1,29 @@
+Index: power-profiles-daemon/src/power-profiles-daemon.c
+===================================================================
+--- power-profiles-daemon.orig/src/power-profiles-daemon.c
++++ power-profiles-daemon/src/power-profiles-daemon.c
+@@ -103,6 +103,13 @@ typedef enum {
+
+ #define PROP_ALL (PROP_ACTIVE_PROFILE | PROP_INHIBITED | PROP_PROFILES | PROP_ACTIONS | PROP_DEGRADED | PROP_ACTIVE_PROFILE_HOLDS)
+
++/* This uses a weird Auto prefix to avoid conflicts with later added polkit types. */
++typedef PolkitAuthorizationResult AutoPolkitAuthorizationResult;
++typedef PolkitSubject AutoPolkitSubject;
++
++G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitAuthorizationResult, g_object_unref)
++G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitSubject, g_object_unref)
++
+ static const char *
+ get_active_profile (PpdApp *data)
+ {
+@@ -603,8 +610,8 @@ check_action_permission (PpdApp
+ GError **error)
+ {
+ g_autoptr(GError) local_error = NULL;
+- g_autoptr(PolkitAuthorizationResult) result = NULL;
+- g_autoptr(PolkitSubject) subject = NULL;
++ g_autoptr(AutoPolkitAuthorizationResult) result = NULL;
++ g_autoptr(AutoPolkitSubject) subject = NULL;
+
+ subject = polkit_system_bus_name_new (sender);
+ result = polkit_authority_check_authorization_sync (data->auth,
diff --git a/debian/patches/series b/debian/patches/series
index e69de29..1a4a0bc 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -0,0 +1 @@
+build_older_polkit.patch
diff --git a/docs/meson.build b/docs/meson.build
index cfd2406..489658b 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -24,6 +24,7 @@ private_headers = [
'ppd-driver-balanced.h',
'ppd-driver-fake.h',
'ppd-driver-intel-pstate.h',
+ 'ppd-driver-placeholder.h',
'ppd-driver-platform-profile.h',
'ppd-driver-power-saver.h',
'ppd-utils.h',
diff --git a/meson.build b/meson.build
index 81181f6..675bb10 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('power-profiles-daemon', [ 'c' ],
- version: '0.8.1',
+ version: '0.10.1',
license: 'GPLv3+',
default_options: [
'buildtype=debugoptimized',
@@ -12,9 +12,6 @@ cc = meson.get_compiler('c')
common_cflags = cc.get_supported_arguments([
'-fgnu89-inline',
- '-fvisibility=hidden',
- '-std=gnu99',
- '-Wall',
'-Wundef',
'-Wunused',
'-Wstrict-prototypes',
@@ -37,13 +34,19 @@ endif
gio_dep = dependency('gio-2.0')
gudev_dep = dependency('gudev-1.0', version: '>= 234')
upower_dep = dependency('upower-glib')
+polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91')
+polkit_policy_directory = polkit_gobject_dep.get_pkgconfig_variable('policydir')
gnome = import('gnome')
add_global_arguments('-D_GNU_SOURCE=1', language: 'c')
+add_global_arguments(common_cflags, language: 'c')
-pylint = find_program('pylint-3', 'pylint3', 'pylint', required: false)
-pylint_flags = ['-d', 'C0116', '-d', 'C0114', '-d', 'W0707']
+if get_option('pylint')
+ pylint = find_program('pylint-3', 'pylint3', 'pylint', required: true)
+ pylint_flags = ['-d', 'C0116', '-d', 'C0114', '-d', 'W0707']
+endif
+xmllint = find_program('xmllint', required: false)
subdir('src')
subdir('data')
diff --git a/meson_options.txt b/meson_options.txt
index a3789f6..7e89619 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,3 +6,7 @@ option('gtk_doc',
type: 'boolean',
value: false,
description: 'Build docs')
+option('pylint',
+ type: 'boolean',
+ value: false,
+ description: 'Run pylint checks, for developers only')
diff --git a/src/meson.build b/src/meson.build
index 318f284..f20e42a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,4 +1,11 @@
-deps = [ gio_dep, gudev_dep, upower_dep ]
+deps = [ gio_dep, gudev_dep, upower_dep, polkit_gobject_dep ]
+
+config_h = configuration_data()
+config_h.set_quoted('VERSION', meson.project_version())
+config_h_files = configure_file(
+ output: 'config.h',
+ configuration: config_h
+)
resources = gnome.compile_resources(
'power-profiles-daemon-resources', 'power-profiles-daemon.gresource.xml',
@@ -65,7 +72,7 @@ script = configure_file(
install_dir: get_option('bindir')
)
-if pylint.found()
+if get_option('pylint')
test('pylint-powerprofilesctl',
pylint,
args: pylint_flags + [ script ],
diff --git a/src/net.hadess.PowerProfiles.xml b/src/net.hadess.PowerProfiles.xml
index fce04a8..fcfc1d4 100644
--- a/src/net.hadess.PowerProfiles.xml
+++ b/src/net.hadess.PowerProfiles.xml
@@ -13,7 +13,7 @@
OS components would typically use the "Profiles" property to construct
their UI (2 or 3 profiles available), and monitor the "ActiveProfile"
- and the "PerformanceInhibited" properties to update that UI. The UI
+ and the "PerformanceDegraded" properties to update that UI. The UI
would try to set the "ActiveProfile" property if the user selected
a different one.
@@ -24,27 +24,78 @@
The object path will be "/net/hadess/PowerProfiles".
-->
<interface name="net.hadess.PowerProfiles">
+
+ <!--
+ HoldProfile:
+
+ This forces the passed profile (either 'power-saver' or 'performance')
+ to be activated until either the caller quits, "ReleaseProfile" is
+ called, or the "ActiveProfile" is changed by the user.
+
+ This should be used programmatically by OS components when, eg. high-
+ performance workloads are started with the "performance" profile, or
+ battery will soon be critically low with the "power-saver" profile.
+
+ When conflicting profiles are requested to be held, the 'power-saver' profile
+ will be activated in preference to the 'performance' profile.
+
+ Those holds will be automatically cancelled if the user manually switches
+ to another profile, and the "ProfileReleased" signal will be emitted.
+ -->
+ <method name="HoldProfile">
+ <arg name="profile" type="s" direction="in"/>
+ <arg name="reason" type="s" direction="in"/>
+ <arg name="application_id" type="s" direction="in" />
+ <arg name="cookie" type="u" direction="out"/>
+ </method>
+
+ <!--
+ ReleaseProfile:
+
+ This removes the hold that was set on a profile.
+ -->
+ <method name="ReleaseProfile">
+ <arg name="cookie" type="u" direction="in"/>
+ </method>
+
+ <!--
+ ProfileReleased:
+
+ This signal will be emitted if the profile is released because the
+ "ActiveProfile" was manually changed. The signal will only be emitted
+ to the process that originally called "HoldProfile".
+ -->
+ <signal name="ProfileReleased">
+ <arg name="cookie" type="u" direction="out"/>
+ </signal>
+
<!--
ActiveProfile:
The type of the currently active profile. It might change automatically
- if the "performance" profile was selected but it got inhibited, in which
- case the "PerformanceInhibited" property will reflect the reason.
+ if a profile is held, using the "HoldProfile" function.
-->
<property name="ActiveProfile" type="s" access="readwrite"/>
<!--
PerformanceInhibited:
- This will be set if the performance power profile is unavailable, with
- the value being used to identify the reason for unavailability. As new
- reasons can be added, it is recommended that front-ends show a generic
+ This property is deprecated, and unused since version 0.9.
+ -->
+ <property name="PerformanceInhibited" type="s" access="read"/>
+
+ <!--
+ PerformanceDegraded:
+
+ This will be set if the performance power profile is running in degraded
+ mode, with the value being used to identify the reason for that degradation.
+ As new reasons can be added, it is recommended that front-ends show a generic
reason if they do not recognise the value. Possible values are:
- "lap-detected" (the computer is sitting on the user's lap)
- "high-operating-temperature" (the computer is close to overheating)
- - "" (the empty string, if not inhibited)
+ - "" (the empty string, if not performance is not degraded)
-->
- <property name="PerformanceInhibited" type="s" access="read"/>
+ <property name="PerformanceDegraded" type="s" access="read"/>
<!--
Profiles:
@@ -60,6 +111,9 @@
Only one of each type of profile will be listed, with the daemon choosing the
more appropriate "driver" for each profile type.
+
+ This list is guaranteed to be sorted in the same order that the profiles
+ are listed above.
-->
<property name="Profiles" type="aa{sv}" access="read"/>
@@ -72,5 +126,15 @@
-->
<property name="Actions" type="as" access="read"/>
+ <!--
+ ActiveProfileHolds:
+
+ A list of dictionaries representing the current profile holds.
+ The keys in the dict are "ApplicationId", "Profile" and "Reason",
+ and correspond to the "application_id", "profile" and "reason" arguments
+ passed to the HoldProfile() method.
+ -->
+ <property name="ActiveProfileHolds" type="aa{sv}" access="read"/>
+
</interface>
</node>
diff --git a/src/power-profiles-daemon.c b/src/power-profiles-daemon.c
index c07d8a5..7128ac8 100644
--- a/src/power-profiles-daemon.c
+++ b/src/power-profiles-daemon.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2014-2016, 2020 Bastien Nocera <hadess at hadess.net>
+ * Copyright (c) 2014-2016, 2020-2021 Bastien Nocera <hadess at hadess.net>
+ * Copyright (c) 2021 David Redondo <kde at david-redondo.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published by
@@ -7,7 +8,10 @@
*
*/
+#include "config.h"
+
#include <locale.h>
+#include <polkit/polkit.h>
#include "power-profiles-daemon-resources.h"
#include "power-profiles-daemon.h"
@@ -27,12 +31,37 @@ typedef struct {
gboolean was_started;
int ret;
+ GKeyFile *config;
+ char *config_path;
+
+ PolkitAuthority *auth;
+
PpdProfile active_profile;
+ PpdProfile selected_profile;
GPtrArray *probed_drivers;
PpdDriver *driver;
GPtrArray *actions;
+ GHashTable *profile_holds;
} PpdApp;
+typedef struct {
+ PpdProfile profile;
+ char *reason;
+ char *application_id;
+ char *requester;
+} ProfileHold;
+
+static void
+profile_hold_free (ProfileHold *hold)
+{
+ if (hold == NULL)
+ return;
+ g_free (hold->reason);
+ g_free (hold->application_id);
+ g_free (hold->requester);
+ g_free (hold);
+}
+
static PpdApp *ppd_app = NULL;
static void stop_profile_drivers (PpdApp *data);
@@ -68,9 +97,11 @@ typedef enum {
PROP_INHIBITED = 1 << 1,
PROP_PROFILES = 1 << 2,
PROP_ACTIONS = 1 << 3,
+ PROP_DEGRADED = 1 << 4,
+ PROP_ACTIVE_PROFILE_HOLDS = 1 << 5
} PropertiesMask;
-#define PROP_ALL (PROP_ACTIVE_PROFILE | PROP_INHIBITED | PROP_PROFILES | PROP_ACTIONS)
+#define PROP_ALL (PROP_ACTIVE_PROFILE | PROP_INHIBITED | PROP_PROFILES | PROP_ACTIONS | PROP_DEGRADED | PROP_ACTIVE_PROFILE_HOLDS)
static const char *
get_active_profile (PpdApp *data)
@@ -79,7 +110,7 @@ get_active_profile (PpdApp *data)
}
static const char *
-get_performance_inhibited (PpdApp *data)
+get_performance_degraded (PpdApp *data)
{
const char *ret;
PpdDriver *driver;
@@ -87,7 +118,7 @@ get_performance_inhibited (PpdApp *data)
driver = GET_DRIVER(PPD_PROFILE_PERFORMANCE);
if (!driver)
return "";
- ret = ppd_driver_get_performance_inhibited (driver);
+ ret = ppd_driver_get_performance_degraded (driver);
g_assert (ret != NULL);
return ret;
}
@@ -136,6 +167,33 @@ get_actions_variant (PpdApp *data)
return g_variant_builder_end (&builder);
}
+static GVariant *
+get_profile_holds_variant (PpdApp *data)
+{
+ GVariantBuilder builder;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+ g_hash_table_iter_init (&iter, data->profile_holds);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ GVariantBuilder asv_builder;
+ ProfileHold *hold = value;
+
+ g_variant_builder_init (&asv_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&asv_builder, "{sv}", "ApplicationId",
+ g_variant_new_string (hold->application_id));
+ g_variant_builder_add (&asv_builder, "{sv}", "Profile",
+ g_variant_new_string (ppd_profile_to_str (hold->profile)));
+ g_variant_builder_add (&asv_builder, "{sv}", "Reason", g_variant_new_string (hold->reason));
+
+ g_variant_builder_add (&builder, "a{sv}", &asv_builder);
+ }
+
+ return g_variant_builder_end (&builder);
+}
+
static void
send_dbus_event (PpdApp *data,
PropertiesMask mask)
@@ -158,7 +216,11 @@ send_dbus_event (PpdApp *data,
}
if (mask & PROP_INHIBITED) {
g_variant_builder_add (&props_builder, "{sv}", "PerformanceInhibited",
- g_variant_new_string (get_performance_inhibited (data)));
+ g_variant_new_string (""));
+ }
+ if (mask & PROP_DEGRADED) {
+ g_variant_builder_add (&props_builder, "{sv}", "PerformanceDegraded",
+ g_variant_new_string (get_performance_degraded (data)));
}
if (mask & PROP_PROFILES) {
g_variant_builder_add (&props_builder, "{sv}", "Profiles",
@@ -168,6 +230,10 @@ send_dbus_event (PpdApp *data,
g_variant_builder_add (&props_builder, "{sv}", "Actions",
get_actions_variant (data));
}
+ if (mask & PROP_ACTIVE_PROFILE_HOLDS) {
+ g_variant_builder_add (&props_builder, "{sv}", "ActiveProfileHolds",
+ get_profile_holds_variant (data));
+ }
props_changed = g_variant_new ("(s at a{sv}@as)", POWER_PROFILES_IFACE_NAME,
g_variant_builder_end (&props_builder),
@@ -181,6 +247,56 @@ send_dbus_event (PpdApp *data,
props_changed, NULL);
}
+static void
+save_configuration (PpdApp *data)
+{
+ g_autoptr(GError) error = NULL;
+
+ g_key_file_set_string (data->config, "State", "Driver", ppd_driver_get_driver_name (data->driver));
+ g_key_file_set_string (data->config, "State", "Profile", ppd_profile_to_str (data->active_profile));
+ if (!g_key_file_save_to_file (data->config, data->config_path, &error))
+ g_warning ("Could not save configuration file '%s': %s", data->config_path, error->message);
+}
+
+static gboolean
+apply_configuration (PpdApp *data)
+{
+ g_autofree char *driver = NULL;
+ g_autofree char *profile_str = NULL;
+ PpdProfile profile;
+
+ driver = g_key_file_get_string (data->config, "State", "Driver", NULL);
+ if (g_strcmp0 (ppd_driver_get_driver_name (data->driver), driver) != 0)
+ return FALSE;
+ profile_str = g_key_file_get_string (data->config, "State", "Profile", NULL);
+ if (profile_str == NULL)
+ return FALSE;
+ profile = ppd_profile_from_str (profile_str);
+ if (profile == PPD_PROFILE_UNSET) {
+ g_debug ("Resetting invalid configuration profile '%s'", profile_str);
+ g_key_file_remove_key (data->config, "State", "Profile", NULL);
+ return FALSE;
+ }
+
+ g_debug ("Applying profile '%s' from configuration file", profile_str);
+ data->active_profile = profile;
+ return TRUE;
+}
+
+static void
+load_configuration (PpdApp *data)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (g_getenv ("UMOCKDEV_DIR") != NULL)
+ data->config_path = g_build_filename (g_getenv ("UMOCKDEV_DIR"), "ppd_test_conf.ini", NULL);
+ else
+ data->config_path = g_strdup ("/var/lib/power-profiles-daemon/state.ini");
+ data->config = g_key_file_new ();
+ if (!g_key_file_load_from_file (data->config, data->config_path, G_KEY_FILE_KEEP_COMMENTS, &error))
+ g_debug ("Could not load configuration file '%s': %s", data->config_path, error->message);
+}
+
static void
actions_activate_profile (GPtrArray *actions,
PpdProfile profile)
@@ -226,6 +342,29 @@ activate_target_profile (PpdApp *data,
actions_activate_profile (data->actions, target_profile);
data->active_profile = target_profile;
+
+ if (reason == PPD_PROFILE_ACTIVATION_REASON_USER ||
+ reason == PPD_PROFILE_ACTIVATION_REASON_INTERNAL)
+ save_configuration (data);
+}
+
+static void
+release_all_profile_holds (PpdApp *data)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, data->profile_holds);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ ProfileHold *hold = value;
+ guint cookie = GPOINTER_TO_UINT (key);
+
+ g_dbus_connection_emit_signal (data->connection, hold->requester, POWER_PROFILES_DBUS_PATH,
+ POWER_PROFILES_IFACE_NAME, "ProfileReleased",
+ g_variant_new ("(u)", cookie), NULL);
+ g_bus_unwatch_name (cookie);
+ }
+ g_hash_table_remove_all (data->profile_holds);
}
static gboolean
@@ -234,6 +373,7 @@ set_active_profile (PpdApp *data,
GError **error)
{
PpdProfile target_profile;
+ guint mask = PROP_ACTIVE_PROFILE;
target_profile = ppd_profile_from_str (profile);
if (target_profile == PPD_PROFILE_UNSET) {
@@ -245,49 +385,64 @@ set_active_profile (PpdApp *data,
if (target_profile == data->active_profile)
return TRUE;
- if (target_profile == PPD_PROFILE_PERFORMANCE &&
- ppd_driver_is_performance_inhibited (GET_DRIVER (PPD_PROFILE_PERFORMANCE))) {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "Profile '%s' is inhibited", profile);
- return FALSE;
- }
-
g_debug ("Transitioning active profile from '%s' to '%s' by user request",
ppd_profile_to_str (data->active_profile), profile);
+ if (g_hash_table_size (data->profile_holds) != 0 ) {
+ g_debug ("Releasing active profile holds");
+ release_all_profile_holds (data);
+ mask |= PROP_ACTIVE_PROFILE_HOLDS;
+ }
+
activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_USER);
- send_dbus_event (data, PROP_ACTIVE_PROFILE);
+ data->selected_profile = target_profile;
+ send_dbus_event (data, mask);
return TRUE;
}
+static PpdProfile
+effective_hold_profile (PpdApp *data)
+{
+ GHashTableIter iter;
+ gpointer value;
+ PpdProfile profile = PPD_PROFILE_UNSET;
+
+ g_hash_table_iter_init (&iter, data->profile_holds);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ ProfileHold *hold = value;
+
+ if (hold->profile == PPD_PROFILE_POWER_SAVER) {
+ profile = PPD_PROFILE_POWER_SAVER;
+ break;
+ }
+ profile = hold->profile;
+ }
+ return profile;
+}
+
static void
-driver_performance_inhibited_changed_cb (GObject *gobject,
- GParamSpec *pspec,
- gpointer user_data)
+driver_performance_degraded_changed_cb (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer user_data)
{
PpdApp *data = user_data;
PpdDriver *driver = PPD_DRIVER (gobject);
const char *prop_str = pspec->name;
- if (g_strcmp0 (prop_str, "performance-inhibited") != 0) {
+ if (g_strcmp0 (prop_str, "performance-degraded") != 0) {
g_warning ("Ignoring '%s' property change on profile driver '%s'",
prop_str, ppd_driver_get_driver_name (driver));
return;
}
if (!(ppd_driver_get_profiles (driver) & PPD_PROFILE_PERFORMANCE)) {
- g_warning ("Ignored 'performance-inhibited' change on non-performance driver '%s'",
+ g_warning ("Ignored 'performance-degraded' change on non-performance driver '%s'",
ppd_driver_get_driver_name (driver));
return;
}
- send_dbus_event (data, PROP_INHIBITED);
- if (!ppd_driver_is_performance_inhibited (driver))
- return;
-
- activate_target_profile (data, PPD_PROFILE_BALANCED, PPD_PROFILE_ACTIVATION_REASON_INHIBITION);
- send_dbus_event (data, PROP_ACTIVE_PROFILE);
+ send_dbus_event (data, PROP_DEGRADED);
}
static void
@@ -308,6 +463,169 @@ driver_profile_changed_cb (PpdDriver *driver,
send_dbus_event (data, PROP_ACTIVE_PROFILE);
}
+static void
+release_profile_hold (PpdApp *data,
+ guint cookie)
+{
+ guint mask = PROP_ACTIVE_PROFILE_HOLDS;
+ ProfileHold *hold;
+ PpdProfile hold_profile, next_profile;
+
+ hold = g_hash_table_lookup (data->profile_holds, GUINT_TO_POINTER (cookie));
+ if (!hold) {
+ g_debug("No hold with cookie %d", cookie);
+ return;
+ }
+
+ g_bus_unwatch_name (cookie);
+ hold_profile = hold->profile;
+ g_hash_table_remove (data->profile_holds, GUINT_TO_POINTER (cookie));
+
+ if (g_hash_table_size (data->profile_holds) == 0 &&
+ hold_profile != data->selected_profile) {
+ g_debug ("No profile holds anymore going back to last manually activated profile");
+ activate_target_profile (data, data->selected_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ mask |= PROP_ACTIVE_PROFILE;
+ } else if (hold_profile == data->active_profile) {
+ next_profile = effective_hold_profile (data);
+ if (next_profile != PPD_PROFILE_UNSET &&
+ next_profile != data->active_profile) {
+ g_debug ("Next profile is %s", ppd_profile_to_str (next_profile));
+ activate_target_profile (data, next_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ mask |= PROP_ACTIVE_PROFILE;
+ }
+ }
+
+ send_dbus_event (data, mask);
+}
+
+static void
+holder_disappeared (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ PpdApp *data = user_data;
+ GHashTableIter iter;
+ gpointer key, value;
+ GPtrArray *cookies;
+ guint i;
+
+ cookies = g_ptr_array_new ();
+ g_hash_table_iter_init (&iter, data->profile_holds);
+ while (g_hash_table_iter_next (&iter, &key, (gpointer *) &value)) {
+ guint cookie = GPOINTER_TO_UINT (key);
+ ProfileHold *hold = value;
+
+ if (g_strcmp0 (hold->requester, name) != 0)
+ continue;
+
+ g_debug ("Holder %s with cookie %u disappeared, adding to list", name, cookie);
+ g_ptr_array_add (cookies, GUINT_TO_POINTER (cookie));
+ }
+
+ for (i = 0; i < cookies->len; i++) {
+ guint cookie = GPOINTER_TO_UINT (cookies->pdata[i]);
+ g_debug ("Removing profile hold for cookie %u", cookie);
+ release_profile_hold (data, cookie);
+ }
+ g_ptr_array_free (cookies, TRUE);
+}
+
+static void
+hold_profile (PpdApp *data,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ const char *profile_name;
+ const char *reason;
+ const char *application_id;
+ PpdProfile profile;
+ ProfileHold *hold;
+ guint watch_id;
+ guint mask;
+
+ g_variant_get (parameters, "(&s&s&s)", &profile_name, &reason, &application_id);
+ profile = ppd_profile_from_str (profile_name);
+ if (profile != PPD_PROFILE_PERFORMANCE &&
+ profile != PPD_PROFILE_POWER_SAVER) {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "Only profiles 'performance' and 'power-saver' can be a hold profile");
+ return;
+ }
+
+ hold = g_new0 (ProfileHold, 1);
+ hold->profile = profile;
+ hold->reason = g_strdup (reason);
+ hold->application_id = g_strdup (application_id);
+ hold->requester = g_strdup (g_dbus_method_invocation_get_sender (invocation));
+
+ g_debug ("%s(%s) requesting to hold profile '%s', reason: '%s'", application_id,
+ hold->requester, profile_name, reason);
+ watch_id = g_bus_watch_name_on_connection (data->connection, hold->requester,
+ G_BUS_NAME_WATCHER_FLAGS_NONE, NULL,
+ holder_disappeared, data, NULL);
+ g_hash_table_insert (data->profile_holds, GUINT_TO_POINTER (watch_id), hold);
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", watch_id));
+ mask = PROP_ACTIVE_PROFILE_HOLDS;
+
+ if (profile != data->active_profile) {
+ PpdProfile target_profile = effective_hold_profile (data);
+ if (target_profile != PPD_PROFILE_UNSET &&
+ target_profile != data->active_profile) {
+ activate_target_profile (data, target_profile, PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD);
+ mask |= PROP_ACTIVE_PROFILE;
+ }
+ }
+
+ send_dbus_event (data, mask);
+}
+
+static void
+release_profile (PpdApp *data,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ guint cookie;
+ g_variant_get (parameters, "(u)", &cookie);
+ if (!g_hash_table_contains (data->profile_holds, GUINT_TO_POINTER (cookie))) {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "No hold with cookie %d", cookie);
+ return;
+ }
+ release_profile_hold (data, cookie);
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static gboolean
+check_action_permission (PpdApp *data,
+ const char *sender,
+ const char *action,
+ GError **error)
+{
+ g_autoptr(GError) local_error = NULL;
+ g_autoptr(PolkitAuthorizationResult) result = NULL;
+ g_autoptr(PolkitSubject) subject = NULL;
+
+ subject = polkit_system_bus_name_new (sender);
+ result = polkit_authority_check_authorization_sync (data->auth,
+ subject,
+ action,
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
+ NULL, &local_error);
+ if (result == NULL ||
+ !polkit_authorization_result_get_is_authorized (result))
+ {
+ g_set_error (error, G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Not Authorized: %s", local_error ? local_error->message : action);
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
static GVariant *
handle_get_property (GDBusConnection *connection,
const gchar *sender,
@@ -324,11 +642,15 @@ handle_get_property (GDBusConnection *connection,
if (g_strcmp0 (property_name, "ActiveProfile") == 0)
return g_variant_new_string (get_active_profile (data));
if (g_strcmp0 (property_name, "PerformanceInhibited") == 0)
- return g_variant_new_string (get_performance_inhibited (data));
+ return g_variant_new_string ("");
if (g_strcmp0 (property_name, "Profiles") == 0)
return get_profiles_variant (data);
if (g_strcmp0 (property_name, "Actions") == 0)
return get_actions_variant (data);
+ if (g_strcmp0 (property_name, "PerformanceDegraded") == 0)
+ return g_variant_new_string (get_performance_degraded (data));
+ if (g_strcmp0 (property_name, "ActiveProfileHolds") == 0)
+ return get_profile_holds_variant (data);
return NULL;
}
@@ -352,14 +674,55 @@ handle_set_property (GDBusConnection *connection,
"No such property: %s", property_name);
return FALSE;
}
+ if (!check_action_permission (data, sender, "net.hadess.PowerProfiles.switch-profile", error))
+ return FALSE;
g_variant_get (value, "&s", &profile);
return set_active_profile (data, profile, error);
}
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ PpdApp *data = user_data;
+ g_assert (data->connection);
+
+ if (g_strcmp0 (interface_name, POWER_PROFILES_IFACE_NAME) != 0) {
+ g_dbus_method_invocation_return_error (invocation,G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE,
+ "Unknown interface %s", interface_name);
+ return;
+ }
+
+ if (g_strcmp0 (method_name, "HoldProfile") == 0) {
+ g_autoptr(GError) local_error = NULL;
+ if (!check_action_permission (data,
+ g_dbus_method_invocation_get_sender (invocation),
+ "net.hadess.PowerProfiles.hold-profile",
+ &local_error)) {
+ g_dbus_method_invocation_return_gerror (invocation, local_error);
+ return;
+ }
+ hold_profile (data, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "ReleaseProfile") == 0) {
+ release_profile (data, parameters, invocation);
+ } else {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
+ "No such method %s in interface %s", interface_name,
+ method_name);
+ }
+}
+
+
static const GDBusInterfaceVTable interface_vtable =
{
- NULL,
+ handle_method_call,
handle_get_property,
handle_set_property
};
@@ -422,6 +785,7 @@ driver_probe_request_cb (PpdDriver *driver,
static void
stop_profile_drivers (PpdApp *data)
{
+ release_all_profile_holds (data);
g_ptr_array_set_size (data->probed_drivers, 0);
g_ptr_array_set_size (data->actions, 0);
g_clear_object (&data->driver);
@@ -431,9 +795,6 @@ static void
start_profile_drivers (PpdApp *data)
{
guint i;
- PpdProfile prev_profile;
-
- prev_profile = data->active_profile;
for (i = 0; i < G_N_ELEMENTS (objects); i++) {
GObject *object;
@@ -462,7 +823,7 @@ start_profile_drivers (PpdApp *data)
continue;
}
- result = ppd_driver_probe (driver, &prev_profile);
+ result = ppd_driver_probe (driver);
if (result == PPD_PROBE_RESULT_FAIL) {
g_debug ("probe() failed for driver %s, skipping",
ppd_driver_get_driver_name (driver));
@@ -477,8 +838,8 @@ start_profile_drivers (PpdApp *data)
data->driver = driver;
- g_signal_connect (G_OBJECT (driver), "notify::performance-inhibited",
- G_CALLBACK (driver_performance_inhibited_changed_cb), data);
+ g_signal_connect (G_OBJECT (driver), "notify::performance-degraded",
+ G_CALLBACK (driver_performance_degraded_changed_cb), data);
g_signal_connect (G_OBJECT (driver), "profile-changed",
G_CALLBACK (driver_profile_changed_cb), data);
} else if (PPD_IS_ACTION (object)) {
@@ -504,13 +865,8 @@ start_profile_drivers (PpdApp *data)
goto bail;
}
- if (prev_profile != data->active_profile) {
- g_debug ("Using '%s' as current profile from probed driver",
- ppd_profile_to_str (prev_profile));
- data->active_profile = prev_profile;
- }
-
- /* Set initial state */
+ /* Set initial state either from configuration, or using the currently selected profile */
+ apply_configuration (data);
activate_target_profile (data, data->active_profile, PPD_PROFILE_ACTIVATION_REASON_RESET);
send_dbus_event (data, PROP_ALL);
@@ -583,9 +939,14 @@ free_app_data (PpdApp *data)
data->name_id = 0;
}
+ g_clear_pointer (&data->config_path, g_free);
+ g_clear_pointer (&data->config, g_key_file_unref);
g_ptr_array_free (data->probed_drivers, TRUE);
g_ptr_array_free (data->actions, TRUE);
g_clear_object (&data->driver);
+ g_hash_table_destroy (data->profile_holds);
+
+ g_clear_object (&data->auth);
g_clear_pointer (&data->main_loop, g_main_loop_unref);
g_clear_pointer (&data->introspection_data, g_dbus_node_info_unref);
@@ -627,11 +988,17 @@ int main (int argc, char **argv)
if (verbose)
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+ g_debug ("Starting power-profiles-daemon version "VERSION);
+
data = g_new0 (PpdApp, 1);
data->main_loop = g_main_loop_new (NULL, TRUE);
+ data->auth = polkit_authority_get_sync (NULL, NULL);
data->probed_drivers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
data->actions = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ data->profile_holds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) profile_hold_free);
data->active_profile = PPD_PROFILE_BALANCED;
+ data->selected_profile = PPD_PROFILE_BALANCED;
+ load_configuration (data);
ppd_app = data;
/* Set up D-Bus */
diff --git a/src/powerprofilesctl.in b/src/powerprofilesctl.in
index b525e91..9b5e201 100755
--- a/src/powerprofilesctl.in
+++ b/src/powerprofilesctl.in
@@ -1,5 +1,7 @@
#!@PYTHON3@
+import signal
+import subprocess
import sys
from gi.repository import Gio, GLib
@@ -15,6 +17,8 @@ def usage_main():
print(' get Print the currently active power profile')
print(' set Set the currently active power profile')
print(' list List available power profiles')
+ print(' list-holds List current power profile holds')
+ print(' launch Launch a command while holding a power profile')
print('')
print('Use “powerprofilesctl help COMMAND” to get detailed help.')
@@ -43,6 +47,28 @@ def usage_list():
print('')
print('List available power profiles.')
+def usage_list_holds():
+ print('Usage:')
+ print(' powerprofilesctl list-holds')
+ print('')
+ print('List current power profile holds.')
+
+def usage_launch():
+ print('Usage:')
+ print(' powerprofilesctl launch [COMMAND…]')
+ print('')
+ print('Launch a command while holding a power profile.')
+ print('')
+ print('Options:')
+ print(' -p, --profile=PROFILE The power profile to hold')
+ print(' -r, --reason=REASON The reason for the profile hold')
+ print(' -i, --appid=APP-ID The application ID for the profile hold')
+ print('')
+ print('Launch the command while holding a power profile, either performance, ')
+ print('or power-saver. By default, the profile hold is for the performance ')
+ print('profile, but it might not be available on all systems. See the list ')
+ print('command for a list of available profiles.')
+
def usage(_command=None):
if not _command:
usage_main()
@@ -52,6 +78,10 @@ def usage(_command=None):
usage_set()
elif _command == 'list':
usage_list()
+ elif _command == 'list-holds':
+ usage_list_holds()
+ elif _command == 'launch':
+ usage_launch()
elif _command == 'version':
usage_version()
else:
@@ -100,8 +130,8 @@ def get_profiles_property(prop):
def _list():
try:
profiles = get_profiles_property('Profiles')
- reason = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'PerformanceInhibited')
- inhibited = (reason != '')
+ reason = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'PerformanceDegraded')
+ degraded = (reason != '')
active = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'ActiveProfile')
except:
print("Couldn\'t get Profiles: ", sys.exc_info()[0])
@@ -111,13 +141,53 @@ def _list():
for profile in reversed(profiles):
if index > 0:
print('')
- print(('%s %s:') % ('*' if profile['Profile'] == active else ' ', profile['Profile']))
+ marker = '*' if profile['Profile'] == active else ' '
+ print(f'{marker} {profile["Profile"]}:')
print(' Driver: ', profile['Driver'])
if profile['Profile'] == 'performance':
- print(' Inhibited: ', f'yes ({reason})' if inhibited else 'no')
+ print(' Degraded: ', f'yes ({reason})' if degraded else 'no')
index += 1
-def main(): # pylint: disable=too-many-branches
+def _list_holds():
+ try:
+ holds = get_profiles_property('ActiveProfileHolds')
+ except:
+ # print("Couldn\'t get ActiveProfileHolds: ", sys.exc_info()[0])
+ raise SystemError
+ else:
+ index = 0
+ for hold in holds:
+ if index > 0:
+ print('')
+ print('Hold:')
+ print(' Profile: ', hold['Profile'])
+ print(' Application ID: ', hold['ApplicationId'])
+ print(' Reason: ', hold['Reason'])
+ index += 1
+
+def _launch(args, profile, appid, reason):
+ try:
+ bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
+ 'net.hadess.PowerProfiles',
+ '/net/hadess/PowerProfiles',
+ 'net.hadess.PowerProfiles', None)
+ except:
+ raise SystemError
+
+ cookie = proxy.HoldProfile('(sss)', profile, reason, appid)
+
+ # Kill child when we go away
+ def receive_signal(_signum, _stack):
+ launched_app.terminate()
+ signal.signal(signal.SIGTERM, receive_signal)
+
+ # print (f'Got {cookie} for {profile} hold')
+ with subprocess.Popen(args) as launched_app:
+ launched_app.wait()
+ proxy.ReleaseProfile('(u)', cookie)
+
+def main(): # pylint: disable=too-many-branches, disable=too-many-statements
args = None
if len(sys.argv) == 1:
command = 'list'
@@ -146,6 +216,47 @@ def main(): # pylint: disable=too-many-branches
_set(args[0])
elif command == 'list':
_list()
+ elif command == 'list-holds':
+ _list_holds()
+ elif command == 'launch':
+ if len(args) == 0:
+ sys.exit(0)
+ profile = None
+ reason = None
+ appid = None
+ while True:
+ if args[0] == '--':
+ args = args[1:]
+ break
+ if args[0][:9] == '--profile' or args[0] == '-p':
+ if args[0][:10] == '--profile=':
+ args = args[0].split('=') + args[1:]
+ profile = args[1]
+ args = args[2:]
+ continue
+ if args[0][:8] == '--reason' or args[0] == '-r':
+ if args[0][:9] == '--reason=':
+ args = args[0].split('=') + args[1:]
+ reason = args[1]
+ args = args[2:]
+ continue
+ if args[0][:7] == '--appid' or args[0] == '-i':
+ if args[0][:8] == '--appid=':
+ args = args[0].split('=') + args[1:]
+ appid = args[1]
+ args = args[2:]
+ continue
+ break
+
+ if len(args) < 1:
+ sys.exit(0)
+ if not appid:
+ appid = args[0]
+ if not reason:
+ reason = 'Running ' + appid
+ if not profile:
+ profile = 'performance'
+ _launch(args, profile, appid, reason)
if __name__ == '__main__':
main()
diff --git a/src/ppd-driver-fake.c b/src/ppd-driver-fake.c
index 431ba64..ecd6154 100644
--- a/src/ppd-driver-fake.c
+++ b/src/ppd-driver-fake.c
@@ -22,7 +22,9 @@ struct _PpdDriverFake
gboolean tio_set;
struct termios old_tio;
- gboolean inhibited;
+ GIOChannel *channel;
+ guint watch_id;
+ gboolean degraded;
};
G_DEFINE_TYPE (PpdDriverFake, ppd_driver_fake, PPD_TYPE_DRIVER)
@@ -46,19 +48,19 @@ ppd_driver_fake_constructor (GType type,
}
static void
-toggle_inhibition (PpdDriverFake *fake)
+toggle_degradation (PpdDriverFake *fake)
{
- fake->inhibited = !fake->inhibited;
+ fake->degraded = !fake->degraded;
g_object_set (G_OBJECT (fake),
- "performance-inhibited", fake->inhibited ? "lap-detected" : NULL,
+ "performance-degraded", fake->degraded ? "lap-detected" : NULL,
NULL);
}
static void
keyboard_usage (void)
{
- g_print ("Valid keys are: i (toggle inhibition), r (restart drivers), q/x (quit)\n");
+ g_print ("Valid keys are: d (toggle degradation), r (restart drivers), q/x (quit)\n");
}
static gboolean
@@ -80,9 +82,9 @@ check_keyboard (GIOChannel *source,
return TRUE;
switch (buf[0]) {
- case 'i':
- g_print ("Toggling inhibition\n");
- toggle_inhibition (fake);
+ case 'd':
+ g_print ("Toggling degradation\n");
+ toggle_degradation (fake);
break;
case 'r':
g_print ("Restarting profile drivers\n");
@@ -103,7 +105,6 @@ check_keyboard (GIOChannel *source,
static gboolean
setup_keyboard (PpdDriverFake *fake)
{
- GIOChannel *channel;
struct termios new_tio;
tcgetattr(STDIN_FILENO, &fake->old_tio);
@@ -111,18 +112,18 @@ setup_keyboard (PpdDriverFake *fake)
new_tio.c_lflag &=(~ICANON & ~ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
- channel = g_io_channel_unix_new (STDIN_FILENO);
- if (!channel) {
+ fake->channel = g_io_channel_unix_new (STDIN_FILENO);
+ if (!fake->channel) {
g_warning ("Failed to open stdin");
return FALSE;
}
- if (g_io_channel_set_encoding (channel, NULL, NULL) != G_IO_STATUS_NORMAL) {
+ if (g_io_channel_set_encoding (fake->channel, NULL, NULL) != G_IO_STATUS_NORMAL) {
g_warning ("Failed to set stdin encoding to NULL");
return FALSE;
}
- g_io_add_watch (channel, G_IO_IN, (GIOFunc) check_keyboard, fake);
+ fake->watch_id = g_io_add_watch (fake->channel, G_IO_IN, (GIOFunc) check_keyboard, fake);
fake->tio_set = TRUE;
return TRUE;
}
@@ -142,8 +143,7 @@ envvar_set (const char *key)
}
static PpdProbeResult
-ppd_driver_fake_probe (PpdDriver *driver,
- PpdProfile *prev_profile)
+ppd_driver_fake_probe (PpdDriver *driver)
{
PpdDriverFake *fake;
@@ -177,6 +177,8 @@ ppd_driver_fake_finalize (GObject *object)
PpdDriverFake *fake;
fake = PPD_DRIVER_FAKE (object);
+ g_clear_pointer (&fake->channel, g_io_channel_unref);
+ g_clear_handle_id (&fake->watch_id, g_source_remove);
if (fake->tio_set)
tcsetattr(STDIN_FILENO, TCSANOW, &fake->old_tio);
G_OBJECT_CLASS (ppd_driver_fake_parent_class)->finalize (object);
diff --git a/src/ppd-driver-intel-pstate.c b/src/ppd-driver-intel-pstate.c
index de84339..5725516 100644
--- a/src/ppd-driver-intel-pstate.c
+++ b/src/ppd-driver-intel-pstate.c
@@ -62,7 +62,7 @@ update_no_turbo (PpdDriverIntelPstate *pstate)
turbo_disabled = TRUE;
}
- g_object_set (G_OBJECT (pstate), "performance-inhibited",
+ g_object_set (G_OBJECT (pstate), "performance-degraded",
turbo_disabled ? "high-operating-temperature" : NULL,
NULL);
}
@@ -107,8 +107,7 @@ open_policy_dir (void)
}
static gboolean
-ppd_driver_intel_pstate_probe (PpdDriver *driver,
- PpdProfile *prev_profile)
+ppd_driver_intel_pstate_probe (PpdDriver *driver)
{
PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver);
g_autoptr(GDir) dir = NULL;
diff --git a/src/ppd-driver-platform-profile.c b/src/ppd-driver-platform-profile.c
index 9e20ee8..8111251 100644
--- a/src/ppd-driver-platform-profile.c
+++ b/src/ppd-driver-platform-profile.c
@@ -59,7 +59,9 @@ profile_to_acpi_platform_profile_value (PpdDriverPlatformProfile *self,
case PPD_PROFILE_POWER_SAVER:
if (g_strv_contains ((const char * const*) self->profile_choices, "low-power"))
return "low-power";
- return "cool";
+ if (g_strv_contains ((const char * const*) self->profile_choices, "cool"))
+ return "cool";
+ return "quiet";
case PPD_PROFILE_BALANCED:
return "balanced";
case PPD_PROFILE_PERFORMANCE:
@@ -101,7 +103,7 @@ read_platform_profile (void)
platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
if (!g_file_get_contents (platform_profile_path,
- &new_profile_str, NULL, NULL)) {
+ &new_profile_str, NULL, &error)) {
g_debug ("Failed to get contents for '%s': %s",
platform_profile_path,
error->message);
@@ -124,7 +126,10 @@ save_platform_profile_choices (PpdDriverPlatformProfile *self)
platform_profile_choices_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_CHOICES_PATH);
if (!g_file_get_contents (platform_profile_choices_path,
- &choices_str, NULL, NULL)) {
+ &choices_str, NULL, &error)) {
+ g_debug ("Failed to get contents for '%s': %s",
+ platform_profile_choices_path,
+ error->message);
return FALSE;
}
@@ -138,7 +143,8 @@ verify_acpi_platform_profile_choices (PpdDriverPlatformProfile *self)
const char * const *choices = (const char * const*) self->profile_choices;
if ((g_strv_contains (choices, "low-power") ||
- g_strv_contains (choices, "cool")) &&
+ g_strv_contains (choices, "cool") ||
+ g_strv_contains (choices, "quiet")) &&
g_strv_contains (choices, "balanced") &&
g_strv_contains (choices, "performance"))
return PPD_PROBE_RESULT_SUCCESS;
@@ -157,9 +163,9 @@ update_dytc_lapmode_state (PpdDriverPlatformProfile *self)
self->lapmode = new_lapmode;
g_debug ("dytc_lapmode is now %s, so profile is %s",
self->lapmode ? "on" : "off",
- self->lapmode ? "inhibited" : "uninhibited");
+ self->lapmode ? "degraded" : "not degraded");
g_object_set (G_OBJECT (self),
- "performance-inhibited", self->lapmode ? "lap-detected" : NULL,
+ "performance-degraded", self->lapmode ? "lap-detected" : NULL,
NULL);
}
@@ -222,13 +228,6 @@ ppd_driver_platform_profile_activate_profile (PpdDriver *drive
return TRUE;
}
- if (profile == PPD_PROFILE_PERFORMANCE &&
- self->lapmode) {
- g_debug ("Can't switch to performance mode, lapmode is detected");
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Mode is inhibited");
- return FALSE;
- }
-
g_signal_handler_block (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
if (!ppd_utils_write (platform_profile_path, profile_to_acpi_platform_profile_value (self, profile), error)) {
@@ -257,8 +256,7 @@ find_dytc (GUdevDevice *dev,
}
static PpdProbeResult
-ppd_driver_platform_profile_probe (PpdDriver *driver,
- PpdProfile *prev_profile)
+ppd_driver_platform_profile_probe (PpdDriver *driver)
{
PpdDriverPlatformProfile *self = PPD_DRIVER_PLATFORM_PROFILE (driver);
g_autoptr(GFile) acpi_platform_profile = NULL;
@@ -293,8 +291,6 @@ ppd_driver_platform_profile_probe (PpdDriver *driver,
return self->probe_result;
}
- *prev_profile = read_platform_profile ();
-
/* Lenovo-specific proximity sensor */
self->device = ppd_utils_find_device ("platform",
(GCompareFunc) find_dytc,
diff --git a/src/ppd-driver.c b/src/ppd-driver.c
index 8ccd305..93abb34 100644
--- a/src/ppd-driver.c
+++ b/src/ppd-driver.c
@@ -16,26 +16,22 @@
* @Title: Profile Drivers
*
* Profile drivers are the implementation of the different profiles for
- * the whole system. A driver will implement support for one or more
- * profiles, usually one or both of the `performance` and `power-saver`
- * profiles, for a particular system. Only one driver will be selected and
- * running per profile.
+ * the whole system. A driver will need to implement support `power-saver`
+ * and `balanced` at a minimum.
*
- * If no system-specific driver is available, some placeholder `balanced`
- * and `power-saver` drivers will be put in place, and the `performance`
- * profile will be unavailable.
+ * If no system-specific driver is available, a placeholder driver
+ * will be put in place, and the `performance` profile will be unavailable.
*
- * Common implementation of drivers might be:
- * - a driver handling all three profiles, relying on a firmware feature
- * exposed in the kernel,
- * - a driver that only implements the `performance` profile on a particular
- * system it has intimate knowledge of, leaving the `balanced` and
- * `power-saver` profiles using placeholder
+ * There should not be a need to implement system-specific drivers, as the
+ * [`platform_profile`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-platform_profile)
+ * kernel API offers a way to implement system-specific profiles which
+ * `power-profiles-daemon` can consume.
*
* When a driver implements the `performance` profile, it might set the
- * #PpdDriver:performance-inhibited property if the profile isn't available for any
- * reason, such as thermal limits being reached, or because a part of the
- * user's body is too close for safety, for example.
+ * #PpdDriver:performance-degraded property if the profile isn't running to
+ * its fullest performance for any reason, such as thermal limits being
+ * reached, or because a part of the user's body is too close for safety,
+ * for example.
*/
typedef struct
@@ -43,14 +39,14 @@ typedef struct
char *driver_name;
PpdProfile profiles;
gboolean selected;
- char *performance_inhibited;
+ char *performance_degraded;
} PpdDriverPrivate;
enum {
PROP_0,
PROP_DRIVER_NAME,
PROP_PROFILES,
- PROP_PERFORMANCE_INHIBITED
+ PROP_PERFORMANCE_DEGRADED
};
enum {
@@ -81,9 +77,9 @@ ppd_driver_set_property (GObject *object,
case PROP_PROFILES:
priv->profiles = g_value_get_flags (value);
break;
- case PROP_PERFORMANCE_INHIBITED:
- g_clear_pointer (&priv->performance_inhibited, g_free);
- priv->performance_inhibited = g_value_dup_string (value);
+ case PROP_PERFORMANCE_DEGRADED:
+ g_clear_pointer (&priv->performance_degraded, g_free);
+ priv->performance_degraded = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
@@ -106,8 +102,8 @@ ppd_driver_get_property (GObject *object,
case PROP_PROFILES:
g_value_set_flags (value, priv->profiles);
break;
- case PROP_PERFORMANCE_INHIBITED:
- g_value_set_string (value, priv->performance_inhibited);
+ case PROP_PERFORMANCE_DEGRADED:
+ g_value_set_string (value, priv->performance_degraded);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
@@ -121,7 +117,7 @@ ppd_driver_finalize (GObject *object)
priv = PPD_DRIVER_GET_PRIVATE (PPD_DRIVER (object));
g_clear_pointer (&priv->driver_name, g_free);
- g_clear_pointer (&priv->performance_inhibited, g_free);
+ g_clear_pointer (&priv->performance_degraded, g_free);
G_OBJECT_CLASS (ppd_driver_parent_class)->finalize (object);
}
@@ -197,15 +193,15 @@ ppd_driver_class_init (PpdDriverClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
- * PpdDriver:performance-inhibited:
+ * PpdDriver:performance-degraded:
*
* If set to a non-%NULL value, the reason why the performance profile is unavailable.
* The value must be one of the options listed in the D-Bus API reference.
*/
- g_object_class_install_property (object_class, PROP_PERFORMANCE_INHIBITED,
- g_param_spec_string("performance-inhibited",
- "Performance Inhibited",
- "Why the performance profile is inhibited, if set",
+ g_object_class_install_property (object_class, PROP_PERFORMANCE_DEGRADED,
+ g_param_spec_string("performance-degraded",
+ "Performance Degraded",
+ "Why the performance profile is degraded, if set",
NULL,
G_PARAM_READWRITE));
}
@@ -216,24 +212,14 @@ ppd_driver_init (PpdDriver *self)
}
PpdProbeResult
-ppd_driver_probe (PpdDriver *driver,
- PpdProfile *previous_profile)
+ppd_driver_probe (PpdDriver *driver)
{
- PpdProfile profile = PPD_PROFILE_UNSET;
- PpdProbeResult ret;
-
g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE);
- g_return_val_if_fail (previous_profile != NULL, FALSE);
if (!PPD_DRIVER_GET_CLASS (driver)->probe)
return PPD_PROBE_RESULT_SUCCESS;
- ret = PPD_DRIVER_GET_CLASS (driver)->probe (driver, &profile);
- if (ret == PPD_PROBE_RESULT_SUCCESS &&
- profile != PPD_PROFILE_UNSET &&
- ppd_profile_has_single_flag (profile))
- *previous_profile = profile;
- return ret;
+ return PPD_DRIVER_GET_CLASS (driver)->probe (driver);
}
gboolean
@@ -285,18 +271,18 @@ ppd_driver_get_selected (PpdDriver *driver)
}
const char *
-ppd_driver_get_performance_inhibited (PpdDriver *driver)
+ppd_driver_get_performance_degraded (PpdDriver *driver)
{
PpdDriverPrivate *priv;
g_return_val_if_fail (PPD_IS_DRIVER (driver), NULL);
priv = PPD_DRIVER_GET_PRIVATE (driver);
- return priv->performance_inhibited ? priv->performance_inhibited : "";
+ return priv->performance_degraded ? priv->performance_degraded : "";
}
gboolean
-ppd_driver_is_performance_inhibited (PpdDriver *driver)
+ppd_driver_is_performance_degraded (PpdDriver *driver)
{
PpdDriverPrivate *priv;
@@ -304,7 +290,7 @@ ppd_driver_is_performance_inhibited (PpdDriver *driver)
priv = PPD_DRIVER_GET_PRIVATE (driver);
- return (priv->performance_inhibited != NULL);
+ return (priv->performance_degraded != NULL);
}
void
@@ -323,14 +309,14 @@ const char *
ppd_profile_activation_reason_to_str (PpdProfileActivationReason reason)
{
switch (reason) {
- case PPD_PROFILE_ACTIVATION_REASON_INHIBITION:
- return "inhibition";
case PPD_PROFILE_ACTIVATION_REASON_INTERNAL:
return "internal";
case PPD_PROFILE_ACTIVATION_REASON_RESET:
return "reset";
case PPD_PROFILE_ACTIVATION_REASON_USER:
return "user";
+ case PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD:
+ return "program-hold";
default:
g_assert_not_reached ();
}
diff --git a/src/ppd-driver.h b/src/ppd-driver.h
index 7885c1e..16977b5 100644
--- a/src/ppd-driver.h
+++ b/src/ppd-driver.h
@@ -35,24 +35,24 @@ typedef enum {
/**
* PpdProfileActivationReason:
- * PPD_PROFILE_ACTIVATION_REASON_INHIBITION: switching profiles because
- * of performance profile inhibition.
- * PPD_PROFILE_ACTIVATION_REASON_INTERNAL: the driver profile changed
+ * @PPD_PROFILE_ACTIVATION_REASON_INTERNAL: the driver profile changed
* internally, usually because of a key combination.
- * PPD_PROFILE_ACTIVATION_REASON_RESET: setting profile on startup, or
+ * @PPD_PROFILE_ACTIVATION_REASON_RESET: setting profile on startup, or
* because drivers are getting reprobed.
- * PPD_PROFILE_ACTIVATION_REASON_USER: setting profile because the user
+ * @PPD_PROFILE_ACTIVATION_REASON_USER: setting profile because the user
* requested it.
+ * @PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD: setting profile because a program
+ * requested it through the `HoldProfile` method.
*
* Those are possible reasons for a profile being activated. Based on those
* reasons, drivers can choose whether or not that changes the effective
* profile internally.
*/
typedef enum{
- PPD_PROFILE_ACTIVATION_REASON_INHIBITION = 0,
- PPD_PROFILE_ACTIVATION_REASON_INTERNAL,
+ PPD_PROFILE_ACTIVATION_REASON_INTERNAL = 0,
PPD_PROFILE_ACTIVATION_REASON_RESET,
- PPD_PROFILE_ACTIVATION_REASON_USER
+ PPD_PROFILE_ACTIVATION_REASON_USER,
+ PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD
} PpdProfileActivationReason;
/**
@@ -68,8 +68,7 @@ struct _PpdDriverClass
{
GObjectClass parent_class;
- PpdProbeResult (* probe) (PpdDriver *driver,
- PpdProfile *previous_profile);
+ PpdProbeResult (* probe) (PpdDriver *driver);
gboolean (* activate_profile) (PpdDriver *driver,
PpdProfile profile,
PpdProfileActivationReason reason,
@@ -77,13 +76,13 @@ struct _PpdDriverClass
};
#ifndef __GTK_DOC_IGNORE__
-PpdProbeResult ppd_driver_probe (PpdDriver *driver, PpdProfile *previous_profile);
+PpdProbeResult ppd_driver_probe (PpdDriver *driver);
gboolean ppd_driver_activate_profile (PpdDriver *driver,
PpdProfile profile, PpdProfileActivationReason reason, GError **error);
const char *ppd_driver_get_driver_name (PpdDriver *driver);
PpdProfile ppd_driver_get_profiles (PpdDriver *driver);
-const char *ppd_driver_get_performance_inhibited (PpdDriver *driver);
-gboolean ppd_driver_is_performance_inhibited (PpdDriver *driver);
+const char *ppd_driver_get_performance_degraded (PpdDriver *driver);
+gboolean ppd_driver_is_performance_degraded (PpdDriver *driver);
void ppd_driver_emit_profile_changed (PpdDriver *driver, PpdProfile profile);
const char *ppd_profile_activation_reason_to_str (PpdProfileActivationReason reason);
#endif
diff --git a/tests/integration-test b/tests/integration-test.py
similarity index 58%
rename from tests/integration-test
rename to tests/integration-test.py
index 3ca5643..22dc42c 100755
--- a/tests/integration-test
+++ b/tests/integration-test.py
@@ -7,6 +7,7 @@
#
# Copyright: (C) 2011 Martin Pitt <martin.pitt at ubuntu.com>
# (C) 2020 Bastien Nocera <hadess at hadess.net>
+# (C) 2021 David Redondo <kde at david-redondo.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -50,6 +51,7 @@ except ImportError:
PP = 'net.hadess.PowerProfiles'
PP_PATH = '/net/hadess/PowerProfiles'
+PP_INTERFACE = 'net.hadess.PowerProfiles'
class Tests(dbusmock.DBusTestCase):
@classmethod
@@ -102,6 +104,10 @@ class Tests(dbusmock.DBusTestCase):
The testbed is initially empty.
'''
self.testbed = UMockdev.Testbed.new()
+ self.polkitd, self.obj_polkit = self.spawn_server_template(
+ 'polkitd', {}, stdout=subprocess.PIPE)
+ self.obj_polkit.SetAllowed(['net.hadess.PowerProfiles.switch-profile',
+ 'net.hadess.PowerProfiles.hold-profile'])
self.proxy = None
self.log = None
@@ -113,7 +119,21 @@ class Tests(dbusmock.DBusTestCase):
def tearDown(self):
del self.testbed
self.stop_daemon()
+
+ if self.polkitd:
+ try:
+ self.polkitd.kill()
+ except OSError:
+ pass
+ self.polkitd.wait()
+ self.polkitd = None
+ self.obj_polkit = None
+
del self.tp_acpi
+ try:
+ os.remove(self.testbed.get_root_dir() + '/' + 'ppd_test_conf.ini')
+ except Exception:
+ pass
# on failures, print daemon log
errors = [x[1] for x in self._outcome.errors if x[1]]
@@ -195,6 +215,15 @@ class Tests(dbusmock.DBusTestCase):
PP_PATH, 'org.freedesktop.DBus.Properties', None)
return proxy.Set('(ssv)', PP, name, value)
+ def call_dbus_method(self, name, parameters):
+ '''Call a method of the daemon D-Bus interface.'''
+
+ proxy = Gio.DBusProxy.new_sync(
+ self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
+ PP_PATH, PP_INTERFACE, None)
+ return proxy.call_sync(name, parameters, Gio.DBusCallFlags.NO_AUTO_START, -1, None)
+
+
def have_text_in_log(self, text):
return self.count_text_in_log(text) > 0
@@ -221,14 +250,28 @@ class Tests(dbusmock.DBusTestCase):
[ 'DEVPATH', '/devices/platform/thinkpad_acpi' ]
)
+ def create_empty_platform_profile(self):
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir)
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
+ profile.write('\n')
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
+ choices.write('\n')
+
def create_platform_profile(self):
acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile") ,'w') as profile:
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
profile.write("performance\n")
- with open(os.path.join(acpi_dir, "platform_profile_choices") ,'w') as choices:
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
choices.write("low-power balanced performance\n")
+ def remove_platform_profile(self):
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.remove(os.path.join(acpi_dir, "platform_profile_choices"))
+ os.remove(os.path.join(acpi_dir, "platform_profile"))
+ os.removedirs(acpi_dir)
+
def assertEventually(self, condition, message=None, timeout=50):
'''Assert that condition function eventually returns True.
@@ -253,7 +296,7 @@ class Tests(dbusmock.DBusTestCase):
'''D-Bus startup error'''
self.start_daemon()
- out = subprocess.run([self.daemon_path], capture_output=True)
+ out = subprocess.run([self.daemon_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.assertEqual(out.returncode, 1, "power-profile-daemon started but should have failed")
self.stop_daemon()
@@ -262,7 +305,7 @@ class Tests(dbusmock.DBusTestCase):
self.start_daemon()
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), '')
+ self.assertEqual(self.get_dbus_property('PerformanceDegraded'), '')
profiles = self.get_dbus_property('Profiles')
self.assertEqual(len(profiles), 2)
@@ -279,8 +322,8 @@ class Tests(dbusmock.DBusTestCase):
self.stop_daemon()
- def test_inhibited_transition(self):
- '''Test that transitions work as expected when inhibited'''
+ def test_inhibited_property(self):
+ '''Test that the inhibited property exists'''
self.create_dytc_device()
self.create_platform_profile()
@@ -288,17 +331,27 @@ class Tests(dbusmock.DBusTestCase):
profiles = self.get_dbus_property('Profiles')
self.assertEqual(len(profiles), 3)
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.assertEqual(self.get_dbus_property('PerformanceInhibited'), '')
+
+ def test_degraded_transition(self):
+ '''Test that transitions work as expected when degraded'''
+
+ self.create_dytc_device()
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property('Profiles')
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('balanced'))
self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- # Inhibit
+ # Degraded
self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '1\n')
self.assertEventually(lambda: self.have_text_in_log('dytc_lapmode is now on'))
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), 'lap-detected')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEqual(self.get_dbus_property('PerformanceDegraded'), 'lap-detected')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
# Switch to non-performance
self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
@@ -310,17 +363,17 @@ class Tests(dbusmock.DBusTestCase):
# Create 2 CPUs with preferences
dir1 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/")
os.makedirs(dir1)
- with open(os.path.join(dir1, "energy_performance_preference") ,'w') as prefs:
+ with open(os.path.join(dir1, "energy_performance_preference"),'w') as prefs:
prefs.write("performance\n")
dir2 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/")
os.makedirs(dir2)
- with open(os.path.join(dir2, "energy_performance_preference") ,'w') as prefs:
+ with open(os.path.join(dir2, "energy_performance_preference"),'w') as prefs:
prefs.write("performance\n")
# Create no_turbo pref
pstate_dir = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate")
os.makedirs(pstate_dir)
- with open(os.path.join(pstate_dir, "no_turbo") ,'w') as no_turbo:
+ with open(os.path.join(pstate_dir, "no_turbo"),'w') as no_turbo:
no_turbo.write("0\n")
self.start_daemon()
@@ -345,12 +398,12 @@ class Tests(dbusmock.DBusTestCase):
self.assertEqual(contents, b'performance')
# Disable turbo
- with open(os.path.join(pstate_dir, "no_turbo") ,'w') as no_turbo:
+ with open(os.path.join(pstate_dir, "no_turbo"),'w') as no_turbo:
no_turbo.write("1\n")
self.assertEventually(lambda: self.have_text_in_log('File monitor change happened for '))
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), 'high-operating-temperature')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.assertEqual(self.get_dbus_property('PerformanceDegraded'), 'high-operating-temperature')
self.stop_daemon()
@@ -368,7 +421,7 @@ class Tests(dbusmock.DBusTestCase):
# Create CPU with preference
dir1 = os.path.join(self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/")
os.makedirs(dir1)
- with open(os.path.join(dir1, "energy_performance_preference") ,'w') as prefs:
+ with open(os.path.join(dir1, "energy_performance_preference"),'w') as prefs:
prefs.write("performance\n")
upowerd, obj_upower = self.spawn_server_template(
@@ -406,33 +459,20 @@ class Tests(dbusmock.DBusTestCase):
self.assertEqual(profiles[0]['Profile'], 'power-saver')
self.assertEqual(profiles[2]['Driver'], 'platform_profile')
self.assertEqual(profiles[2]['Profile'], 'performance')
+ self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- # lapmode detected, but performance wasn't selected anyway
+ # lapmode detected
self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '1\n')
- self.assertEventually(lambda: self.get_dbus_property('PerformanceInhibited') == 'lap-detected')
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEventually(lambda: self.get_dbus_property('PerformanceDegraded') == 'lap-detected')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
# Reset lapmode
self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '0\n')
- self.assertEventually(lambda: self.get_dbus_property('PerformanceInhibited') == '')
+ self.assertEventually(lambda: self.get_dbus_property('PerformanceDegraded') == '')
- # Set performance mode
- self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('performance'))
+ # Performance mode didn't change
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.assertEventually(lambda: self.read_sysfs_file("sys/firmware/acpi/platform_profile") == b'performance')
-
- # And turn on lapmode
- self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '1\n')
- self.assertEventually(lambda: self.read_sysfs_file("sys/firmware/acpi/platform_profile") == b'balanced')
-
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), 'lap-detected')
-
- # Turn off lapmode, profile stays balanced
- self.testbed.set_attribute(self.tp_acpi, 'dytc_lapmode', '0\n')
- self.assertEventually(lambda: self.get_dbus_property('PerformanceInhibited') == '')
- self.assertEventually(lambda: self.read_sysfs_file("sys/firmware/acpi/platform_profile") == b'balanced')
# Switch to power-saver mode
self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
@@ -488,21 +528,16 @@ class Tests(dbusmock.DBusTestCase):
def test_platform_driver_late_load(self):
'''Test that we can handle the platform_profile driver getting loaded late'''
- acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
- os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile") ,'w') as profile:
- profile.write('\n')
- with open(os.path.join(acpi_dir, "platform_profile_choices") ,'w') as choices:
- choices.write('\n')
-
+ self.create_empty_platform_profile()
self.start_daemon()
profiles = self.get_dbus_property('Profiles')
self.assertEqual(len(profiles), 2)
- with open(os.path.join(acpi_dir, "platform_profile_choices") ,'w') as choices:
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
choices.write("low-power\nbalanced\nperformance\n")
- with open(os.path.join(acpi_dir, "platform_profile") ,'w') as profile:
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
profile.write("performance\n")
# Wait for profiles to get reloaded
@@ -510,8 +545,8 @@ class Tests(dbusmock.DBusTestCase):
profiles = self.get_dbus_property('Profiles')
self.assertEqual(len(profiles), 3)
# Was set in platform_profile before we loaded the drivers
- self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
- self.assertEqual(self.get_dbus_property('PerformanceInhibited'), '')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEqual(self.get_dbus_property('PerformanceDegraded'), '')
self.stop_daemon()
@@ -520,9 +555,9 @@ class Tests(dbusmock.DBusTestCase):
# Uses cool instead of low-power
acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
os.makedirs(acpi_dir)
- with open(os.path.join(acpi_dir, "platform_profile") ,'w') as profile:
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
profile.write("cool\n")
- with open(os.path.join(acpi_dir, "platform_profile_choices") ,'w') as choices:
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
choices.write("cool balanced performance\n")
self.start_daemon()
@@ -530,14 +565,232 @@ class Tests(dbusmock.DBusTestCase):
self.assertEqual(len(profiles), 3)
self.assertEqual(profiles[0]['Driver'], 'platform_profile')
self.assertEqual(profiles[0]['Profile'], 'power-saver')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'balanced')
+ self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'cool')
- # Check that we can set the power-saver/cool profile again
+ self.stop_daemon()
+
+ def test_quiet(self):
+ # Uses cool instead of low-power
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ os.makedirs(acpi_dir)
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
+ profile.write("cool\n")
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
+ choices.write("quiet balanced balanced-performance performance\n")
+
+ self.start_daemon()
+ profiles = self.get_dbus_property('Profiles')
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(profiles[0]['Driver'], 'platform_profile')
+ self.assertEqual(profiles[0]['Profile'], 'power-saver')
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'balanced')
+ self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'quiet')
+
+ self.stop_daemon()
+
+ def test_hold_release_profile(self):
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property('Profiles')
+ self.assertEqual(len(profiles), 3)
+
+ cookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', 'testReason', 'testApplication')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ profileHolds = self.get_dbus_property('ActiveProfileHolds')
+ self.assertEqual(len(profileHolds), 1)
+ self.assertEqual(profileHolds[0]["Profile"], "performance")
+ self.assertEqual(profileHolds[0]["Reason"], "testReason")
+ self.assertEqual(profileHolds[0]["ApplicationId"], "testApplication")
+
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", cookie))
+ profileHolds = self.get_dbus_property('ActiveProfileHolds')
+ self.assertEqual(len(profileHolds), 0)
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # When the profile is changed manually, holds should be released a
+ self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 1)
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+
self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('balanced'))
+ self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 0)
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # When all holds are released, the last manually selected profile should be activated
self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
- self.assertEqual(self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b'cool')
+ cookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", cookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+
+ self.stop_daemon()
+
+ def test_vanishing_hold(self):
+ self.create_platform_profile()
+ self.start_daemon()
+
+ builddir = os.getenv('top_builddir', '.')
+ tool_path = os.path.join(builddir, 'src', 'powerprofilesctl')
+
+ launch_process = subprocess.Popen([tool_path, 'launch', '-p', 'power-saver', 'sleep', '3600'],
+ stdout=sys.stdout, stderr=sys.stderr)
+ assert launch_process
+ time.sleep(1)
+ holds = self.get_dbus_property('ActiveProfileHolds')
+ self.assertEqual(len(holds), 1)
+ hold = holds[0]
+ self.assertEqual(hold['Profile'], 'power-saver')
+
+ # Make sure to handle vanishing clients
+ launch_process.terminate()
+ launch_process.wait()
+
+ holds = self.get_dbus_property('ActiveProfileHolds')
+ self.assertEqual(len(holds), 0)
+
+ self.stop_daemon()
+
+ def test_hold_priority(self):
+ '''power-saver should take priority over performance'''
+ self.create_platform_profile()
+ self.start_daemon()
+
+ profiles = self.get_dbus_property('Profiles')
+ self.assertEqual(len(profiles), 3)
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # Test every order of holding and releasing power-saver and performance
+ # hold performance and then power-saver, release in the same order
+ performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", powerSaverCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # hold performance and then power-saver, but release power-saver first
+ performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",powerSaverCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # hold power-saver and then performance, release in the same order
+ powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",powerSaverCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", performanceCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ # hold power-saver and then performance, but release performance first
+ powerSaverCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('power-saver', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)",performanceCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.call_dbus_method('ReleaseProfile', GLib.Variant("(u)", powerSaverCookie))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ self.stop_daemon()
+
+ def test_save_profile(self):
+ '''save profile across runs'''
+
+ self.create_platform_profile()
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
+ self.stop_daemon()
+
+ # sys.stderr.write('\n-------------- config file: ----------------\n')
+ # with open(self.testbed.get_root_dir() + '/' + 'ppd_test_conf.ini') as f:
+ # sys.stderr.write(f.read())
+ # sys.stderr.write('------------------------------\n')
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ # Programmatically set profile aren't saved
+ performanceCookie = self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'performance')
+ self.stop_daemon()
+
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'power-saver')
+ self.stop_daemon()
+
+ def test_save_deferred_load(self):
+ '''save profile across runs, but kernel driver loaded after start'''
+
+ self.create_platform_profile()
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.set_dbus_property('ActiveProfile', GLib.Variant.new_string('power-saver'))
+ self.stop_daemon()
+ self.remove_platform_profile()
+
+ # We could verify the contents of the configuration file here
+
+ self.create_empty_platform_profile()
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/")
+ with open(os.path.join(acpi_dir, "platform_profile_choices"),'w') as choices:
+ choices.write("low-power\nbalanced\nperformance\n")
+ with open(os.path.join(acpi_dir, "platform_profile"),'w') as profile:
+ profile.write("performance\n")
+
+ self.assertEventually(lambda: self.get_dbus_property('ActiveProfile') == 'power-saver')
+ self.stop_daemon()
+
+ def test_not_allowed_profile(self):
+ '''Check that we get errors when trying to change a profile and not allowed'''
+
+ self.obj_polkit.SetAllowed(dbus.Array([], signature='s'))
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ proxy = Gio.DBusProxy.new_sync(
+ self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, PP,
+ PP_PATH, 'org.freedesktop.DBus.Properties', None)
+ with self.assertRaises(gi.repository.GLib.GError) as cm:
+ proxy.Set('(ssv)', PP, 'ActiveProfile', GLib.Variant.new_string('power-saver'))
+ self.assertIn('AccessDenied', str(cm.exception))
+
+ self.stop_daemon()
+
+ def test_not_allowed_hold(self):
+ '''Check that we get an error when trying to hold a profile and not allowed'''
+
+ self.obj_polkit.SetAllowed(dbus.Array([], signature='s'))
+ self.start_daemon()
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+
+ with self.assertRaises(gi.repository.GLib.GError) as cm:
+ self.call_dbus_method('HoldProfile', GLib.Variant("(sss)", ('performance', '', '')))
+ self.assertIn('AccessDenied', str(cm.exception))
+
+ self.assertEqual(self.get_dbus_property('ActiveProfile'), 'balanced')
+ self.assertEqual(len(self.get_dbus_property('ActiveProfileHolds')), 0)
self.stop_daemon()
diff --git a/tests/meson.build b/tests/meson.build
index 462c3aa..21d3152 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,10 +1,18 @@
-integration_test = find_program('integration-test')
-
envs = environment()
envs.set ('top_builddir', meson.build_root())
envs.set ('top_srcdir', meson.source_root())
-test('power-profiles-daemon-integration-test',
- integration_test,
- env: envs
- )
+python3 = find_program('python3')
+unittest_inspector = find_program('unittest_inspector.py')
+r = run_command(unittest_inspector, files('integration-test.py'))
+unit_tests = r.stdout().strip().split('\n')
+
+foreach ut: unit_tests
+ ut_args = files('integration-test.py')
+ ut_args += ut
+ test(ut,
+ python3,
+ args: ut_args,
+ env: envs,
+ )
+endforeach
diff --git a/tests/unittest_inspector.py b/tests/unittest_inspector.py
new file mode 100755
index 0000000..0d5d3a6
--- /dev/null
+++ b/tests/unittest_inspector.py
@@ -0,0 +1,46 @@
+#! /usr/bin/env python3
+# Copyright © 2020, Canonical Ltd
+#
+# This program 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 program 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, see <http://www.gnu.org/licenses/>.
+# Authors:
+# Marco Trevisan <marco.trevisan at canonical.com>
+
+import argparse
+import importlib.util
+import inspect
+import os
+import unittest
+
+def list_tests(module):
+ tests = []
+ for name, obj in inspect.getmembers(module):
+ if inspect.isclass(obj) and issubclass(obj, unittest.TestCase):
+ cases = unittest.defaultTestLoader.getTestCaseNames(obj)
+ tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ]
+ return tests
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('unittest_source', type=argparse.FileType('r'))
+
+ args = parser.parse_args()
+ source_path = args.unittest_source.name
+ spec = importlib.util.spec_from_file_location(
+ os.path.basename(source_path), source_path)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+
+ for machine, human in list_tests(module):
+ print(human)
More information about the Neon-commits
mailing list