diff options
-rw-r--r-- | debian/changelog | 7 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/device.c | 53 | ||||
-rw-r--r-- | src/ib-brightness-powerd-control.c | 181 | ||||
-rw-r--r-- | src/ib-brightness-powerd-control.h | 42 | ||||
-rw-r--r-- | src/service.c | 178 | ||||
-rw-r--r-- | tests/Makefile.am | 7 | ||||
-rw-r--r-- | tests/test-device.cc | 160 |
8 files changed, 543 insertions, 87 deletions
diff --git a/debian/changelog b/debian/changelog index dbb6ea0..6ef71bf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +indicator-power (12.10.6+14.04.20140326-0ubuntu1) trusty; urgency=low + + [ Y.C cheng ] + * Set brightness via powerd if it exist (using dbus) (LP: #1287599) + + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 26 Mar 2014 18:50:46 +0000 + indicator-power (12.10.6+14.04.20140314-0ubuntu1) trusty; urgency=low [ Charles Kerr ] diff --git a/src/Makefile.am b/src/Makefile.am index be746db..2461592 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,8 @@ libindciatorpower_upower_a_LDFLAGS = $(COVERAGE_LDFLAGS) libindicatorpower_service_a_SOURCES = \ ib-brightness-control.c \ ib-brightness-control.h \ + ib-brightness-powerd-control.c \ + ib-brightness-powerd-control.h \ device-provider.c \ device-provider.h \ device.c \ diff --git a/src/device.c b/src/device.c index ed3c399..e3b655a 100644 --- a/src/device.c +++ b/src/device.c @@ -595,10 +595,12 @@ get_expanded_time_remaining (const IndicatorPowerDevice * device) if (p->state == UP_DEVICE_STATE_CHARGING) { + /* TRANSLATORS: H:MM (hours, minutes) to charge the battery. Example: "1:30 to charge" */ str = g_strdup_printf (_("%0d:%02d to charge"), hours, minutes); } else // discharging { + /* TRANSLATORS: H:MM (hours, minutes) to discharge the battery. Example: "1:30 left"*/ str = g_strdup_printf (_("%0d:%02d left"), hours, minutes); } } @@ -631,22 +633,38 @@ get_accessible_time_remaining (const IndicatorPowerDevice * device) if (p->state == UP_DEVICE_STATE_CHARGING) { if (hours > 0) - str = g_strdup_printf (_("%d %s %d %s to charge"), - hours, g_dngettext (NULL, "hour", "hours", hours), - minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + { + /* TRANSLATORS: "X (hour,hours) Y (minute,minutes) to charge" the battery. + Example: "1 hour 10 minutes to charge" */ + str = g_strdup_printf (_("%d %s %d %s to charge"), + hours, g_dngettext (NULL, "hour", "hours", hours), + minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + } else - str = g_strdup_printf (_("%d %s to charge"), - minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + { + /* TRANSLATORS: "Y (minute,minutes) to charge" the battery. + Example: "59 minutes to charge" */ + str = g_strdup_printf (_("%d %s to charge"), + minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + } } else // discharging { if (hours > 0) - str = g_strdup_printf (_("%d %s %d %s left"), - hours, g_dngettext (NULL, "hour", "hours", hours), - minutes, g_dngettext (NULL, "minute", "minutes", minutes)); - else - str = g_strdup_printf (_("%d %s left"), - minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + { + /* TRANSLATORS: "X (hour,hours) Y (minute,minutes) left" until the battery's empty. + Example: "1 hour 10 minutes left" */ + str = g_strdup_printf (_("%d %s %d %s left"), + hours, g_dngettext (NULL, "hour", "hours", hours), + minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + } + else + { + /* TRANSLATORS: "Y (minute,minutes) left" until the battery's empty. + Example: "59 minutes left" */ + str = g_strdup_printf (_("%d %s left"), + minutes, g_dngettext (NULL, "minute", "minutes", minutes)); + } } } else @@ -700,6 +718,7 @@ get_menuitem_text (const IndicatorPowerDevice * device, if (p->state == UP_DEVICE_STATE_FULLY_CHARGED) { + /* TRANSLATORS: example: "battery (charged)" */ str = g_strdup_printf (_("%s (charged)"), kind_str); } else @@ -715,9 +734,14 @@ get_menuitem_text (const IndicatorPowerDevice * device, } if (time_str && *time_str) - str = g_strdup_printf (_("%s (%s)"), kind_str, time_str); + { + /* TRANSLATORS: example: "battery (time remaining)" */ + str = g_strdup_printf (_("%s (%s)"), kind_str, time_str); + } else - str = g_strdup (kind_str); + { + str = g_strdup (kind_str); + } g_free (time_str); } @@ -783,14 +807,17 @@ indicator_power_device_get_readable_title (const IndicatorPowerDevice * device, if (want_time && want_percent) { + /* TRANSLATORS: after the icon, a time-remaining string + battery %. Example: "(0:59, 33%)" */ str = g_strdup_printf (_("(%s, %.0lf%%)"), time_str, p->percentage); } else if (want_time) { + /* TRANSLATORS: after the icon, a time-remaining string Example: "(0:59)" */ str = g_strdup_printf (_("(%s)"), time_str); } else if (want_percent) { + /* TRANSLATORS: after the icon, a battery %. Example: "(33%)" */ str = g_strdup_printf (_("(%.0lf%%)"), p->percentage); } else diff --git a/src/ib-brightness-powerd-control.c b/src/ib-brightness-powerd-control.c new file mode 100644 index 0000000..2136fcd --- /dev/null +++ b/src/ib-brightness-powerd-control.c @@ -0,0 +1,181 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * 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 the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Yuan-Chen Cheng <yc.cheng@canonical.com> + */ + +#include "ib-brightness-powerd-control.h" + +static gboolean getBrightnessParams(GDBusProxy* powerd_proxy, int *min, int *max, + int *dflt, gboolean *ab_supported); + +GDBusProxy* +powerd_get_proxy(brightness_params_t *params) +{ + GError *error = NULL; + gboolean ret; + + g_return_val_if_fail (params != NULL, NULL); + + GDBusProxy* powerd_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "com.canonical.powerd", + "/com/canonical/powerd", + "com.canonical.powerd", + NULL, + &error); + + if (error != NULL) + { + g_debug ("could not connect to powerd: %s", error->message); + g_error_free (error); + return NULL; + } + + ret = getBrightnessParams(powerd_proxy, &(params->min), &(params->max), + &(params->dflt), &(params->ab_supported)); + + if (! ret) + { + g_debug ("can't get brightness parameters from powerd"); + g_object_unref (powerd_proxy); + return NULL; + } + + return powerd_proxy; +} + + +static gboolean +getBrightnessParams(GDBusProxy* powerd_proxy, int *min, int *max, int *dflt, gboolean *ab_supported) +{ + GVariant *ret = NULL; + GError *error = NULL; + + ret = g_dbus_proxy_call_sync(powerd_proxy, + "getBrightnessParams", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 400, NULL, &error); // timeout: 400 ms + if (!ret) + { + if (error != NULL) + { + if (!g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) + { + g_warning("getBrightnessParams from powerd failed: %s", error->message); + } + g_error_free(error); + } + return FALSE; + } + + g_variant_get(ret, "((iiib))", min, max, dflt, ab_supported); + g_variant_unref(ret); + return TRUE; +} + +static gboolean setUserBrightness(GDBusProxy* powerd_proxy, GCancellable *gcancel, int brightness) +{ + GVariant *ret = NULL; + GError *error = NULL; + + ret = g_dbus_proxy_call_sync(powerd_proxy, + "setUserBrightness", + g_variant_new("(i)", brightness), + G_DBUS_CALL_FLAGS_NONE, + -1, gcancel, &error); + if (!ret) { + g_warning("setUserBrightness via powerd failed: %s", error->message); + g_error_free(error); + return FALSE; + } else { + g_variant_unref(ret); + return TRUE; + } +} + +struct _IbBrightnessPowerdControl +{ + GDBusProxy *powerd_proxy; + GCancellable *gcancel; + + int min; + int max; + int dflt; // defalut value + gboolean ab_supported; + + int current; +}; + +IbBrightnessPowerdControl* +ib_brightness_powerd_control_new (GDBusProxy* powerd_proxy, brightness_params_t params) +{ + IbBrightnessPowerdControl *control; + + control = g_new0 (IbBrightnessPowerdControl, 1); + control->powerd_proxy = powerd_proxy; + control->gcancel = g_cancellable_new(); + + control->min = params.min; + control->max = params.max; + control->dflt = params.dflt; + control->ab_supported = params.ab_supported; + + // XXX: set the brightness value is the only way to sync the brightness value with + // powerd, and we should set the user prefered / last set brightness value upon startup. + // Before we have code to store last set brightness value or other mechanism, we set + // it to default brightness that powerd proposed. + ib_brightness_powerd_control_set_value(control, control->dflt); + + return control; +} + +void +ib_brightness_powerd_control_set_value (IbBrightnessPowerdControl* self, gint value) +{ + gboolean ret; + + value = CLAMP(value, self->min, self->max); + ret = setUserBrightness(self->powerd_proxy, self->gcancel, value); + if (ret) + { + self->current = value; + } +} + +gint +ib_brightness_powerd_control_get_value (IbBrightnessPowerdControl* self) +{ + return self->current; +} + +gint +ib_brightness_powerd_control_get_max_value (IbBrightnessPowerdControl* self) +{ + return self->max; +} + +void +ib_brightness_powerd_control_free (IbBrightnessPowerdControl *self) +{ + g_cancellable_cancel (self->gcancel); + g_object_unref (self->gcancel); + g_object_unref (self->powerd_proxy); + g_free (self); +} + diff --git a/src/ib-brightness-powerd-control.h b/src/ib-brightness-powerd-control.h new file mode 100644 index 0000000..77f6c8d --- /dev/null +++ b/src/ib-brightness-powerd-control.h @@ -0,0 +1,42 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * 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 the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Y.C Cheng <yc.cheng@canonical.com> + */ + +#ifndef __IB_BRIGHTNESS_POWERD_CONTROL_H__ +#define __IB_BRIGHTNESS_POWERD_CONTROL_H__ + +#include <gio/gio.h> + +typedef struct { + int max; + int min; + int dflt; + gboolean ab_supported; +} brightness_params_t; + +GDBusProxy* powerd_get_proxy(brightness_params_t *); + +typedef struct _IbBrightnessPowerdControl IbBrightnessPowerdControl; + +IbBrightnessPowerdControl* ib_brightness_powerd_control_new (GDBusProxy* powerd_proxy, brightness_params_t params); +void ib_brightness_powerd_control_set_value (IbBrightnessPowerdControl* self, gint value); +gint ib_brightness_powerd_control_get_value (IbBrightnessPowerdControl* self); +gint ib_brightness_powerd_control_get_max_value (IbBrightnessPowerdControl* self); +void ib_brightness_powerd_control_free (IbBrightnessPowerdControl *self); + +#endif diff --git a/src/service.c b/src/service.c index 1a4e24a..00efe9b 100644 --- a/src/service.c +++ b/src/service.c @@ -27,6 +27,7 @@ #include "device.h" #include "device-provider.h" #include "ib-brightness-control.h" +#include "ib-brightness-powerd-control.h" #include "service.h" #define BUS_NAME "com.canonical.indicator.power" @@ -104,6 +105,7 @@ struct _IndicatorPowerServicePrivate GSettings * settings; IbBrightnessControl * brightness_control; + IbBrightnessPowerdControl * brightness_powerd_control; guint own_id; guint actions_export_id; @@ -459,7 +461,16 @@ create_phone_devices_section (IndicatorPowerService * self G_GNUC_UNUSED) static void get_brightness_range (IndicatorPowerService * self, gint * low, gint * high) { - const int max = ib_brightness_control_get_max_value (self->priv->brightness_control); + priv_t * p = self->priv; + int max = 0; + if (p->brightness_control) + { + max = ib_brightness_control_get_max_value (self->priv->brightness_control); + } + else if (p->brightness_powerd_control) + { + max = ib_brightness_powerd_control_get_max_value (self->priv->brightness_powerd_control); + } *low = max * 0.05; /* 5% minimum -- don't let the screen go completely dark */ *high = max; } @@ -502,7 +513,15 @@ static GVariant * action_state_for_brightness (IndicatorPowerService * self) { priv_t * p = self->priv; - const gint brightness = ib_brightness_control_get_value (p->brightness_control); + gint brightness = 0; + if (p->brightness_control) + { + brightness = ib_brightness_control_get_value (p->brightness_control); + } + else if (p->brightness_powerd_control) + { + brightness = ib_brightness_powerd_control_get_value (p->brightness_powerd_control); + } return g_variant_new_double (brightness_to_percentage (self, brightness)); } @@ -521,7 +540,16 @@ on_brightness_change_requested (GSimpleAction * action G_GNUC_UNUSED, IndicatorPowerService * self = INDICATOR_POWER_SERVICE (gself); const gdouble percentage = g_variant_get_double (parameter); const int brightness = percentage_to_brightness (self, percentage); - ib_brightness_control_set_value (self->priv->brightness_control, brightness); + + if (self->priv->brightness_control) + { + ib_brightness_control_set_value (self->priv->brightness_control, brightness); + } + else if (self->priv->brightness_powerd_control) + { + ib_brightness_powerd_control_set_value (self->priv->brightness_powerd_control, brightness); + } + update_brightness_action_state (self); } @@ -1018,7 +1046,9 @@ my_dispose (GObject * o) g_clear_object (&p->conn); + // g_clear_pointer has NULL check inside. g_clear_pointer (&p->brightness_control, ib_brightness_control_free); + g_clear_pointer (&p->brightness_powerd_control, ib_brightness_powerd_control_free); indicator_power_service_set_device_provider (self, NULL); @@ -1032,6 +1062,8 @@ my_dispose (GObject * o) static void indicator_power_service_init (IndicatorPowerService * self) { + GDBusProxy *powerd_proxy; + brightness_params_t powerd_brightness_params; priv_t * p = G_TYPE_INSTANCE_GET_PRIVATE (self, INDICATOR_TYPE_POWER_SERVICE, IndicatorPowerServicePrivate); @@ -1041,7 +1073,15 @@ indicator_power_service_init (IndicatorPowerService * self) p->settings = g_settings_new ("com.canonical.indicator.power"); - p->brightness_control = ib_brightness_control_new (); + powerd_proxy = powerd_get_proxy(&powerd_brightness_params); + if (powerd_proxy != NULL) + { + p->brightness_powerd_control = ib_brightness_powerd_control_new(powerd_proxy, powerd_brightness_params); + } + else + { + p->brightness_control = ib_brightness_control_new (); + } init_gactions (self); @@ -1137,6 +1177,129 @@ indicator_power_service_set_device_provider (IndicatorPowerService * self, } } +/* If a device has multiple batteries and uses only one of them at a time, + they should be presented as separate items inside the battery menu, + but everywhere else they should be aggregated (bug 880881). + Their percentages should be averaged. If any are discharging, + the aggregated time remaining should be the maximum of the times + for all those that are discharging, plus the sum of the times + for all those that are idle. Otherwise, the aggregated time remaining + should be the the maximum of the times for all those that are charging. */ +static IndicatorPowerDevice * +create_totalled_battery_device (const GList * devices) +{ + const GList * l; + guint n_charged = 0; + guint n_charging = 0; + guint n_discharging = 0; + guint n_batteries = 0; + double sum_percent = 0; + time_t max_discharge_time = 0; + time_t max_charge_time = 0; + time_t sum_charged_time = 0; + IndicatorPowerDevice * device = NULL; + + for (l=devices; l!=NULL; l=l->next) + { + const IndicatorPowerDevice * device = INDICATOR_POWER_DEVICE(l->data); + + if (indicator_power_device_get_kind(device) == UP_DEVICE_KIND_BATTERY) + { + const double percent = indicator_power_device_get_percentage (device); + const time_t t = indicator_power_device_get_time (device); + const UpDeviceState state = indicator_power_device_get_state (device); + + ++n_batteries; + + if (percent > 0.01) + sum_percent += percent; + + if (state == UP_DEVICE_STATE_CHARGING) + { + ++n_charging; + max_charge_time = MAX(max_charge_time, t); + } + else if (state == UP_DEVICE_STATE_DISCHARGING) + { + ++n_discharging; + max_discharge_time = MAX(max_discharge_time, t); + } + else if (state == UP_DEVICE_STATE_FULLY_CHARGED) + { + ++n_charged; + sum_charged_time += t; + } + } + } + + if (n_batteries > 1) + { + const double percent = sum_percent / n_batteries; + UpDeviceState state; + time_t time_left; + + if (n_discharging > 0) + { + state = UP_DEVICE_STATE_DISCHARGING; + time_left = max_discharge_time + sum_charged_time; + } + else if (n_charging > 0) + { + state = UP_DEVICE_STATE_CHARGING; + time_left = max_charge_time; + } + else if (n_charged > 0) + { + state = UP_DEVICE_STATE_FULLY_CHARGED; + time_left = 0; + } + else + { + state = UP_DEVICE_STATE_UNKNOWN; + time_left = 0; + } + + device = indicator_power_device_new (NULL, + UP_DEVICE_KIND_BATTERY, + percent, + state, + time_left); + } + + return device; +} + +/** + * If there are multiple UP_DEVICE_KIND_BATTERY devices in the list, + * they're merged into a new 'totalled' device representing the sum of them. + * + * Returns: (element-type IndicatorPowerDevice)(transfer full): a list of devices + */ +static GList* +merge_batteries_together (GList * devices) +{ + GList * ret; + IndicatorPowerDevice * merged_device; + + if ((merged_device = create_totalled_battery_device (devices))) + { + GList * l; + + ret = g_list_append (NULL, merged_device); + + for (l=devices; l!=NULL; l=l->next) + if (indicator_power_device_get_kind(INDICATOR_POWER_DEVICE(l->data)) != UP_DEVICE_KIND_BATTERY) + ret = g_list_append (ret, g_object_ref(l->data)); + } + else /* not enough batteries to merge */ + { + ret = g_list_copy (devices); + g_list_foreach (ret, (GFunc)g_object_ref, NULL); + } + + return ret; +} + IndicatorPowerDevice * indicator_power_service_choose_primary_device (GList * devices) { @@ -1144,13 +1307,10 @@ indicator_power_service_choose_primary_device (GList * devices) if (devices != NULL) { - GList * tmp; - - tmp = g_list_copy (devices); + GList * tmp = merge_batteries_together (devices); tmp = g_list_sort (tmp, device_compare_func); primary = g_object_ref (tmp->data); - - g_list_free (tmp); + g_list_free_full (tmp, (GDestroyNotify)g_object_unref); } return primary; diff --git a/tests/Makefile.am b/tests/Makefile.am index 47b3e84..483bb8c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,6 +25,13 @@ AM_CXXFLAGS = $(GTEST_CXXFLAGS) ### tests: indicator-power-device ### +dear-reader-please-note-the-next-test-takes-90-seconds: + @echo "#!/bin/bash" > $@ + @echo "exit 0" >> $@ + @chmod +x $@ +TESTS += dear-reader-please-note-the-next-test-takes-90-seconds +CLEANFILES += dear-reader-please-note-the-next-test-takes-90-seconds + TEST_LIBS = $(COVERAGE_LDFLAGS) libgtest.a -lpthread TEST_CPPFLAGS = $(SERVICE_DEPS_CFLAGS) $(AM_CPPFLAGS) diff --git a/tests/test-device.cc b/tests/test-device.cc index 4d4a89f..96bea80 100644 --- a/tests/test-device.cc +++ b/tests/test-device.cc @@ -19,6 +19,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <gio/gio.h> #include <gtest/gtest.h> +#include <cmath> // ceil() #include "device.h" #include "service.h" @@ -668,77 +669,106 @@ TEST_F(DeviceTest, Inestimable___this_takes_80_seconds) g_free (real_lang); } - -/* The menu title should tell you at a glance what you need to know most: what - device will lose power soonest (and optionally when), or otherwise which - device will take longest to charge (and optionally how long it will take). */ +/* If a device has multiple batteries and uses only one of them at a time, + they should be presented as separate items inside the battery menu, + but everywhere else they should be aggregated (bug 880881). + Their percentages should be averaged. If any are discharging, + the aggregated time remaining should be the maximum of the times + for all those that are discharging, plus the sum of the times + for all those that are idle. Otherwise, the aggregated time remaining + should be the the maximum of the times for all those that are charging. */ TEST_F(DeviceTest, ChoosePrimary) { - GList * device_list; - IndicatorPowerDevice * a; - IndicatorPowerDevice * b; - - a = indicator_power_device_new ("/org/freedesktop/UPower/devices/mouse", - UP_DEVICE_KIND_MOUSE, - 0.0, - UP_DEVICE_STATE_DISCHARGING, - 0); - b = indicator_power_device_new ("/org/freedesktop/UPower/devices/battery", - UP_DEVICE_KIND_BATTERY, - 0.0, - UP_DEVICE_STATE_DISCHARGING, - 0); - - /* device states + time left to {discharge,charge} + % of charge left, - sorted in order of preference wrt the spec's criteria. - So tests[i] should be picked over any test with an index greater than i */ - struct { - int kind; - int state; + struct Description + { + const char * path; + UpDeviceKind kind; + UpDeviceState state; guint64 time; double percentage; - } tests[] = { - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 49, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 50, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 50, 100.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 51, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 50, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 49, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 49, 100.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 48, 50.0 }, - { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_FULLY_CHARGED, 0, 50.0 }, - { UP_DEVICE_KIND_KEYBOARD, UP_DEVICE_STATE_FULLY_CHARGED, 0, 50.0 }, - { UP_DEVICE_KIND_LINE_POWER, UP_DEVICE_STATE_UNKNOWN, 0, 0.0 } }; - device_list = NULL; - device_list = g_list_append (device_list, a); - device_list = g_list_append (device_list, b); + const Description descriptions[] = { + { "/some/path/d0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 60.0 }, // 0 + { "/some/path/d1", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 20, 80.0 }, // 1 + { "/some/path/d2", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 100.0 }, // 2 - for (int i=0, n=G_N_ELEMENTS(tests); i<n; i++) - { - for (int j=i+1; j<n; j++) - { - g_object_set (a, INDICATOR_POWER_DEVICE_KIND, tests[i].kind, - INDICATOR_POWER_DEVICE_STATE, tests[i].state, - INDICATOR_POWER_DEVICE_TIME, guint64(tests[i].time), - INDICATOR_POWER_DEVICE_PERCENTAGE, tests[i].percentage, - NULL); - g_object_set (b, INDICATOR_POWER_DEVICE_KIND, tests[j].kind, - INDICATOR_POWER_DEVICE_STATE, tests[j].state, - INDICATOR_POWER_DEVICE_TIME, guint64(tests[j].time), - INDICATOR_POWER_DEVICE_PERCENTAGE, tests[j].percentage, - NULL); - ASSERT_EQ (a, indicator_power_service_choose_primary_device(device_list)); - g_object_unref (G_OBJECT(a)); - - /* reverse the list to check that list order doesn't matter */ - device_list = g_list_reverse (device_list); - ASSERT_EQ (a, indicator_power_service_choose_primary_device(device_list)); - g_object_unref (G_OBJECT(a)); - } - } + { "/some/path/c0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 10, 60.0 }, // 3 + { "/some/path/c1", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 20, 80.0 }, // 4 + { "/some/path/c2", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 100.0 }, // 5 - // cleanup - g_list_free_full (device_list, g_object_unref); + { "/some/path/f0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_FULLY_CHARGED, 0, 100.0 }, // 6 + { "/some/path/m0", UP_DEVICE_KIND_MOUSE, UP_DEVICE_STATE_DISCHARGING, 20, 80.0 }, // 7 + { "/some/path/m1", UP_DEVICE_KIND_MOUSE, UP_DEVICE_STATE_FULLY_CHARGED, 0, 100.0 }, // 8 + { "/some/path/pw", UP_DEVICE_KIND_LINE_POWER, UP_DEVICE_STATE_UNKNOWN, 0, 0.0 } // 9 + }; + + std::vector<IndicatorPowerDevice*> devices; + for(const auto& desc : descriptions) + devices.push_back(indicator_power_device_new(desc.path, desc.kind, desc.percentage, desc.state, desc.time)); + + const struct { + std::vector<int> device_indices; + Description expected; + } tests[] = { + + { { 0 }, descriptions[0] }, // 1 discharging + { { 0, 1 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 20, 70.0 } }, // 2 discharging + { { 1, 2 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 90.0 } }, // 2 discharging + { { 0, 1, 2 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 80.0 } }, // 3 discharging + + { { 3 }, descriptions[3] }, // 1 charging + { { 3, 4 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 20, 70.0 } }, // 2 charging + { { 4, 5 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 90.0 } }, // 2 charging + { { 3, 4, 5 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 80.0 } }, // 3 charging + + { { 6 }, descriptions[6] }, // 1 charged + { { 6, 0 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 80.0 } }, // 1 charged, 1 discharging + { { 6, 3 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 10, 80.0 } }, // 1 charged, 1 charging + { { 6, 0, 3 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 73.3 } }, // 1 charged, 1 charging, 1 discharging + + { { 0, 7 }, descriptions[0] }, // 1 discharging battery, 1 discharging mouse. pick the one with the least time left. + { { 2, 7 }, descriptions[7] }, // 1 discharging battery, 1 discharging mouse. pick the one with the least time left. + + { { 0, 8 }, descriptions[0] }, // 1 discharging battery, 1 fully-charged mouse. pick the one that's discharging. + { { 6, 7 }, descriptions[7] }, // 1 discharging mouse, 1 fully-charged battery. pick the one that's discharging. + + { { 0, 9 }, descriptions[0] }, // everything comes before power lines + { { 3, 9 }, descriptions[3] }, + { { 7, 9 }, descriptions[7] } + }; + + for(const auto& test : tests) + { + const auto& x = test.expected; + + GList * device_glist = nullptr; + for(const auto& i : test.device_indices) + device_glist = g_list_append(device_glist, devices[i]); + + auto primary = indicator_power_service_choose_primary_device(device_glist); + EXPECT_STREQ(x.path, indicator_power_device_get_object_path(primary)); + EXPECT_EQ(x.kind, indicator_power_device_get_kind(primary)); + EXPECT_EQ(x.state, indicator_power_device_get_state(primary)); + EXPECT_EQ(x.time, indicator_power_device_get_time(primary)); + EXPECT_EQ((int)ceil(x.percentage), (int)ceil(indicator_power_device_get_percentage(primary))); + g_object_unref(primary); + + // reverse the list and repeat the test + // to confirm that list order doesn't matter + device_glist = g_list_reverse (device_glist); + primary = indicator_power_service_choose_primary_device (device_glist); + EXPECT_STREQ(x.path, indicator_power_device_get_object_path(primary)); + EXPECT_EQ(x.kind, indicator_power_device_get_kind(primary)); + EXPECT_EQ(x.state, indicator_power_device_get_state(primary)); + EXPECT_EQ(x.time, indicator_power_device_get_time(primary)); + EXPECT_EQ((int)ceil(x.percentage), (int)ceil(indicator_power_device_get_percentage(primary))); + g_object_unref(primary); + + // cleanup + g_list_free(device_glist); + } + + for (auto& device : devices) + g_object_unref (device); } |