aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/com.canonical.indicator.sound.gschema.xml15
-rw-r--r--src/CMakeLists.txt33
-rw-r--r--src/accounts-service-access.vala235
-rw-r--r--src/main.c108
-rw-r--r--src/service.vala23
-rw-r--r--src/sound-menu.vala48
-rw-r--r--src/volume-control-pulse.vala153
-rw-r--r--tests/integration/indicator-sound-test-base.cpp32
-rw-r--r--tests/integration/indicator-sound-test-base.h4
-rw-r--r--tests/integration/test-indicator.cpp65
-rw-r--r--tests/notifications-test.cc1122
-rw-r--r--tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp15
-rw-r--r--tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h4
-rw-r--r--tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml1
-rw-r--r--tests/sound-menu.cc32
-rw-r--r--tests/volume-control-test.cc109
16 files changed, 1126 insertions, 873 deletions
diff --git a/data/com.canonical.indicator.sound.gschema.xml b/data/com.canonical.indicator.sound.gschema.xml
index 8408883..06bfe74 100644
--- a/data/com.canonical.indicator.sound.gschema.xml
+++ b/data/com.canonical.indicator.sound.gschema.xml
@@ -59,10 +59,10 @@
<description>
How long to remember a user's approval of the confirmation dialog discussed in the
description of 'warning-volume-enabled'.
-
+
The default value (72,000 seconds) corresponds to the 20 hours suggested by
EU standard EN 60950-1/Al2: “The acknowledgement does not need to be repeated
- more than once every 20 h of cumulative listening time.”
+ more than once every 20 h of cumulative listening time.”
</description>
</key>
<key name="warning-volume-decibels" type="d">
@@ -102,16 +102,5 @@
</description>
</key>
- <key name="last-running-player" type="s">
- <default>""</default>
- <summary>Stores which was the last running music player.</summary>
- <description>
- To make the last running player persistent and be able to set its playback controls
- we store which was the last player running id.
-
- The default value ("") corresponds to no player.
- </description>
- </key>
-
</schema>
</schemalist>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ff03859..0498903 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -69,29 +69,37 @@ vala_add(indicator-sound-service
media-player-list
mpris2-interfaces
accounts-service-user
+ accounts-service-access
)
vala_add(indicator-sound-service
options.vala
DEPENDS
volume-control
volume-control-pulse
+ accounts-service-access
)
vala_add(indicator-sound-service
options-gsettings.vala
DEPENDS
options
- volume-control-pulse
+ volume-control-pulse
volume-control
+ accounts-service-access
)
vala_add(indicator-sound-service
volume-control.vala
DEPENDS
options
- volume-control-pulse
+ volume-control-pulse
+ accounts-service-access
+)
+vala_add(indicator-sound-service
+ accounts-service-access.vala
)
vala_add(indicator-sound-service
volume-control-pulse.vala
DEPENDS
+ accounts-service-access
options
volume-control
)
@@ -99,20 +107,22 @@ vala_add(indicator-sound-service
volume-warning.vala
DEPENDS
options
- volume-control-pulse
+ volume-control-pulse
volume-control
warn-notification
- notification
+ notification
+ accounts-service-access
)
vala_add(indicator-sound-service
volume-warning-pulse.vala
DEPENDS
volume-warning
- options
- volume-control-pulse
- volume-control
- warn-notification
- notification
+ options
+ volume-control-pulse
+ volume-control
+ warn-notification
+ notification
+ accounts-service-access
)
vala_add(indicator-sound-service
media-player.vala
@@ -161,8 +171,9 @@ vala_add(indicator-sound-service
DEPENDS
media-player
volume-control
- options
- volume-control-pulse
+ options
+ volume-control-pulse
+ accounts-service-access
)
vala_add(indicator-sound-service
accounts-service-user.vala
diff --git a/src/accounts-service-access.vala b/src/accounts-service-access.vala
new file mode 100644
index 0000000..2c73922
--- /dev/null
+++ b/src/accounts-service-access.vala
@@ -0,0 +1,235 @@
+/*
+ * -*- Mode:Vala; indent-tabs-mode:t; tab-width:4; encoding:utf8 -*-
+ * Copyright 2016 Canonical Ltd.
+ *
+ * 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
+ * the Free Software Foundation; version 3.
+ *
+ * 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 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:
+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
+ */
+
+using PulseAudio;
+using Notify;
+using Gee;
+
+[DBus (name="com.canonical.UnityGreeter.List")]
+interface GreeterListInterfaceAccess : Object
+{
+ public abstract async string get_active_entry () throws IOError;
+ public signal void entry_selected (string entry_name);
+}
+
+public class AccountsServiceAccess : Object
+{
+ private DBusProxy _user_proxy;
+ private GreeterListInterfaceAccess _greeter_proxy;
+ private double _volume = 0.0;
+ private string _last_running_player = "";
+ private bool _mute = false;
+ private Cancellable _dbus_call_cancellable;
+
+ public AccountsServiceAccess ()
+ {
+ _dbus_call_cancellable = new Cancellable ();
+ setup_accountsservice.begin ();
+ }
+
+ ~AccountsServiceAccess ()
+ {
+ _dbus_call_cancellable.cancel ();
+ }
+
+ public string last_running_player
+ {
+ get
+ {
+ return _last_running_player;
+ }
+ set
+ {
+ sync_last_running_player_to_accountsservice.begin (value);
+ }
+ }
+
+ public bool mute
+ {
+ get
+ {
+ return _mute;
+ }
+ set
+ {
+ sync_mute_to_accountsservice.begin (value);
+ }
+ }
+
+ public double volume
+ {
+ get
+ {
+ return _volume;
+ }
+ set
+ {
+ sync_volume_to_accountsservice.begin (value);
+ }
+ }
+
+ /* AccountsService operations */
+ private void accountsservice_props_changed_cb (DBusProxy proxy, Variant changed_properties, string[]? invalidated_properties)
+ {
+ Variant volume_variant = changed_properties.lookup_value ("Volume", VariantType.DOUBLE);
+ if (volume_variant != null) {
+ var volume = volume_variant.get_double ();
+ if (volume >= 0 && _volume != volume) {
+ _volume = volume;
+ this.notify_property("volume");
+ }
+ }
+
+ Variant mute_variant = changed_properties.lookup_value ("Muted", VariantType.BOOLEAN);
+ if (mute_variant != null) {
+ _mute = mute_variant.get_boolean ();
+ this.notify_property("mute");
+ }
+
+ Variant last_running_player_variant = changed_properties.lookup_value ("LastRunningPlayer", VariantType.STRING);
+ if (last_running_player_variant != null) {
+ _last_running_player = last_running_player_variant.get_string ();
+ this.notify_property("last-running-player");
+ }
+ }
+
+ private async void setup_user_proxy (string? username_in = null)
+ {
+ var username = username_in;
+ _user_proxy = null;
+
+ // Look up currently selected greeter user, if asked
+ if (username == null) {
+ try {
+ username = yield _greeter_proxy.get_active_entry ();
+ if (username == "" || username == null)
+ return;
+ } catch (GLib.Error e) {
+ warning ("unable to find Accounts path for user %s: %s", username == null ? "null" : username, e.message);
+ return;
+ }
+ }
+
+ // Get master AccountsService object
+ DBusProxy accounts_proxy;
+ try {
+ accounts_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, null, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts");
+ } catch (GLib.Error e) {
+ warning ("unable to get greeter proxy: %s", e.message);
+ return;
+ }
+
+ // Find user's AccountsService object
+ try {
+ var user_path_variant = yield accounts_proxy.call ("FindUserByName", new Variant ("(s)", username), DBusCallFlags.NONE, -1);
+ string user_path;
+ if (user_path_variant.check_format_string ("(o)", true)) {
+ user_path_variant.get ("(o)", out user_path);
+ _user_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, "org.freedesktop.Accounts", user_path, "com.ubuntu.AccountsService.Sound");
+ } else {
+ warning ("Unable to find user name after calling FindUserByName. Expected type: %s and obtained %s", "(o)", user_path_variant.get_type_string () );
+ return;
+ }
+ } catch (GLib.Error e) {
+ warning ("unable to find Accounts path for user %s: %s", username, e.message);
+ return;
+ }
+
+ // Get current values and listen for changes
+ _user_proxy.g_properties_changed.connect (accountsservice_props_changed_cb);
+ try {
+ var props_variant = yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "GetAll", new Variant ("(s)", _user_proxy.get_interface_name ()), null, DBusCallFlags.NONE, -1);
+ if (props_variant.check_format_string ("(@a{sv})", true)) {
+ Variant props;
+ props_variant.get ("(@a{sv})", out props);
+ accountsservice_props_changed_cb(_user_proxy, props, null);
+ } else {
+ warning ("Unable to get accounts service properties after calling GetAll. Expected type: %s and obtained %s", "(@a{sv})", props_variant.get_type_string () );
+ return;
+ }
+ } catch (GLib.Error e) {
+ debug("Unable to get properties for user %s at first try: %s", username, e.message);
+ }
+ }
+
+ private void greeter_user_changed (string username)
+ {
+ setup_user_proxy.begin (username);
+ }
+
+ private async void setup_accountsservice ()
+ {
+ if (Environment.get_variable ("XDG_SESSION_CLASS") == "greeter") {
+ try {
+ _greeter_proxy = yield Bus.get_proxy (BusType.SESSION, "com.canonical.UnityGreeter", "/list");
+ } catch (GLib.Error e) {
+ warning ("unable to get greeter proxy: %s", e.message);
+ return;
+ }
+ _greeter_proxy.entry_selected.connect (greeter_user_changed);
+ yield setup_user_proxy ();
+ } else {
+ // We are in a user session. We just need our own proxy
+ unowned string username = Environment.get_variable ("USER");
+ if (username != null && username != "") {
+ yield setup_user_proxy (username);
+ }
+ }
+ }
+
+ private async void sync_last_running_player_to_accountsservice (string last_running_player)
+ {
+ if (_user_proxy == null)
+ return;
+
+ try {
+ yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "LastRunningPlayer", new Variant ("s", last_running_player)), null, DBusCallFlags.NONE, -1, _dbus_call_cancellable);
+ } catch (GLib.Error e) {
+ warning ("unable to sync last running player %s to AccountsService: %s",last_running_player, e.message);
+ }
+ _last_running_player = last_running_player;
+ }
+
+ private async void sync_volume_to_accountsservice (double volume)
+ {
+ if (_user_proxy == null)
+ return;
+
+ try {
+ yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Volume", new Variant ("d", volume)), null, DBusCallFlags.NONE, -1, _dbus_call_cancellable);
+ } catch (GLib.Error e) {
+ warning ("unable to sync volume %f to AccountsService: %s", volume, e.message);
+ }
+ }
+
+ private async void sync_mute_to_accountsservice (bool mute)
+ {
+ if (_user_proxy == null)
+ return;
+
+ try {
+ yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Muted", new Variant ("b", mute)), null, DBusCallFlags.NONE, -1, _dbus_call_cancellable);
+ } catch (GLib.Error e) {
+ warning ("unable to sync mute %s to AccountsService: %s", mute ? "true" : "false", e.message);
+ }
+ }
+}
+
+
diff --git a/src/main.c b/src/main.c
index f4a59b2..43e330d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -27,9 +27,9 @@ static pa_glib_mainloop * pgloop = NULL;
static gboolean
sigterm_handler (gpointer data)
{
- g_debug("Got SIGTERM");
- g_main_loop_quit((GMainLoop *)data);
- return G_SOURCE_REMOVE;
+ g_debug("Got SIGTERM");
+ g_main_loop_quit((GMainLoop *)data);
+ return G_SOURCE_REMOVE;
}
static void
@@ -37,8 +37,8 @@ on_name_lost(GDBusConnection * connection,
const gchar * name,
gpointer user_data)
{
- g_warning("Name lost or unable to acquire bus: %s", name);
- g_main_loop_quit((GMainLoop *)user_data);
+ g_warning("Name lost or unable to acquire bus: %s", name);
+ g_main_loop_quit((GMainLoop *)user_data);
}
static void
@@ -46,66 +46,68 @@ on_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- MediaPlayerList * playerlist = NULL;
- IndicatorSoundOptions * options = NULL;
- VolumeControlPulse * volume = NULL;
- AccountsServiceUser * accounts = NULL;
- VolumeWarning * warning = NULL;
-
-
- if (g_strcmp0("lightdm", g_get_user_name()) == 0) {
- playerlist = MEDIA_PLAYER_LIST(media_player_list_greeter_new());
- } else {
- playerlist = MEDIA_PLAYER_LIST(media_player_list_mpris_new());
- accounts = accounts_service_user_new();
- }
-
- pgloop = pa_glib_mainloop_new(NULL);
- options = indicator_sound_options_gsettings_new();
- volume = volume_control_pulse_new(options, pgloop);
- warning = volume_warning_pulse_new(options, pgloop);
-
- service = indicator_sound_service_new (playerlist, volume, accounts, options, warning);
-
- g_clear_object(&playerlist);
- g_clear_object(&options);
- g_clear_object(&volume);
- g_clear_object(&accounts);
- g_clear_object(&warning);
+ MediaPlayerList * playerlist = NULL;
+ IndicatorSoundOptions * options = NULL;
+ VolumeControlPulse * volume = NULL;
+ AccountsServiceUser * accounts = NULL;
+ VolumeWarning * warning = NULL;
+ AccountsServiceAccess * accounts_service_access = NULL;
+
+
+ if (g_strcmp0("lightdm", g_get_user_name()) == 0) {
+ playerlist = MEDIA_PLAYER_LIST(media_player_list_greeter_new());
+ } else {
+ playerlist = MEDIA_PLAYER_LIST(media_player_list_mpris_new());
+ accounts = accounts_service_user_new();
+ }
+
+ pgloop = pa_glib_mainloop_new(NULL);
+ options = indicator_sound_options_gsettings_new();
+ accounts_service_access = accounts_service_access_new();
+ volume = volume_control_pulse_new(options, pgloop, accounts_service_access);
+ warning = volume_warning_pulse_new(options, pgloop);
+
+ service = indicator_sound_service_new (playerlist, volume, accounts, options, warning, accounts_service_access);
+
+ g_clear_object(&playerlist);
+ g_clear_object(&options);
+ g_clear_object(&volume);
+ g_clear_object(&accounts);
+ g_clear_object(&warning);
}
int
main (int argc, char ** argv)
{
- GMainLoop * loop = NULL;
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
- setlocale (LC_ALL, "");
- bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ GMainLoop * loop = NULL;
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
- /* Build Mainloop */
- loop = g_main_loop_new(NULL, FALSE);
+ /* Build Mainloop */
+ loop = g_main_loop_new(NULL, FALSE);
- g_unix_signal_add(SIGTERM, sigterm_handler, loop);
+ g_unix_signal_add(SIGTERM, sigterm_handler, loop);
- /* Initialize libnotify */
- notify_init ("indicator-sound");
+ /* Initialize libnotify */
+ notify_init ("indicator-sound");
- g_bus_own_name(G_BUS_TYPE_SESSION,
- "com.canonical.indicator.sound",
- G_BUS_NAME_OWNER_FLAGS_NONE,
- on_bus_acquired,
- NULL, /* name acquired */
- on_name_lost,
- loop,
- NULL);
+ g_bus_own_name(G_BUS_TYPE_SESSION,
+ "com.canonical.indicator.sound",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ NULL, /* name acquired */
+ on_name_lost,
+ loop,
+ NULL);
- g_main_loop_run(loop);
+ g_main_loop_run(loop);
- g_clear_object(&service);
- g_clear_pointer(&pgloop, pa_glib_mainloop_free);
+ g_clear_object(&service);
+ g_clear_pointer(&pgloop, pa_glib_mainloop_free);
- notify_uninit();
+ notify_uninit();
- return 0;
+ return 0;
}
diff --git a/src/service.vala b/src/service.vala
index cb30820..bdc4d40 100644
--- a/src/service.vala
+++ b/src/service.vala
@@ -20,7 +20,9 @@
public class IndicatorSound.Service: Object {
DBusConnection bus;
- public Service (MediaPlayerList playerlist, VolumeControl volume, AccountsServiceUser? accounts, Options options, VolumeWarning volume_warning) {
+ public Service (MediaPlayerList playerlist, VolumeControl volume, AccountsServiceUser? accounts, Options options, VolumeWarning volume_warning, AccountsServiceAccess? accounts_service_access) {
+
+ _accounts_service_access = accounts_service_access;
try {
bus = Bus.get_sync(GLib.BusType.SESSION);
@@ -60,7 +62,6 @@ public class IndicatorSound.Service: Object {
headphones = false;
break;
}
- message("setting _volume_warning.headphones_active to %d", (int)headphones);
_volume_warning.headphones_active = headphones;
update_root_icon();
@@ -91,12 +92,11 @@ public class IndicatorSound.Service: Object {
this.actions.add_action (this.create_high_volume_action ());
this.actions.add_action (this.create_volume_sync_action ());
- string last_player = this.settings.get_string ("last-running-player");
this.menus = new HashTable<string, SoundMenu> (str_hash, str_equal);
- this.menus.insert ("desktop_greeter", new SoundMenu (null, SoundMenu.DisplayFlags.SHOW_MUTE | SoundMenu.DisplayFlags.HIDE_PLAYERS | SoundMenu.DisplayFlags.GREETER_PLAYERS, last_player));
- this.menus.insert ("phone_greeter", new SoundMenu (null, SoundMenu.DisplayFlags.SHOW_SILENT_MODE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS | SoundMenu.DisplayFlags.GREETER_PLAYERS, last_player));
- this.menus.insert ("desktop", new SoundMenu ("indicator.desktop-settings", SoundMenu.DisplayFlags.SHOW_MUTE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS | SoundMenu.DisplayFlags.ADD_PLAY_CONTROL_INACTIVE_PLAYER, last_player));
- this.menus.insert ("phone", new SoundMenu ("indicator.phone-settings", SoundMenu.DisplayFlags.SHOW_SILENT_MODE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS, last_player));
+ this.menus.insert ("desktop_greeter", new SoundMenu (null, SoundMenu.DisplayFlags.SHOW_MUTE | SoundMenu.DisplayFlags.HIDE_PLAYERS | SoundMenu.DisplayFlags.GREETER_PLAYERS));
+ this.menus.insert ("phone_greeter", new SoundMenu (null, SoundMenu.DisplayFlags.SHOW_SILENT_MODE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS | SoundMenu.DisplayFlags.GREETER_PLAYERS));
+ this.menus.insert ("desktop", new SoundMenu ("indicator.desktop-settings", SoundMenu.DisplayFlags.SHOW_MUTE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS_PLAY_CONTROLS | SoundMenu.DisplayFlags.ADD_PLAY_CONTROL_INACTIVE_PLAYER));
+ this.menus.insert ("phone", new SoundMenu ("indicator.phone-settings", SoundMenu.DisplayFlags.SHOW_SILENT_MODE | SoundMenu.DisplayFlags.HIDE_INACTIVE_PLAYERS));
this.menus.@foreach ( (profile, menu) => {
this.volume_control.bind_property ("active-mic", menu, "show-mic-volume", BindingFlags.SYNC_CREATE);
@@ -112,7 +112,13 @@ public class IndicatorSound.Service: Object {
this.menus.@foreach ( (profile, menu) => {
menu.last_player_updated.connect ((player_id) => {
- this.settings.set_value ("last-running-player", player_id);
+ this._accounts_service_access.last_running_player = player_id;
+ });
+ });
+
+ this._accounts_service_access.notify["last-running-player"].connect(() => {
+ this.menus.@foreach ( (profile, menu) => {
+ menu.set_default_player (this._accounts_service_access.last_running_player);
});
});
@@ -199,6 +205,7 @@ public class IndicatorSound.Service: Object {
private Options _options;
private VolumeWarning _volume_warning;
private IndicatorSound.InfoNotification _info_notification = new IndicatorSound.InfoNotification();
+ private AccountsServiceAccess _accounts_service_access;
const double volume_step_percentage = 0.06;
diff --git a/src/sound-menu.vala b/src/sound-menu.vala
index 630dca0..2ef089a 100644
--- a/src/sound-menu.vala
+++ b/src/sound-menu.vala
@@ -38,7 +38,7 @@ public class SoundMenu: Object
const string PLAYBACK_ITEM_TYPE = "com.canonical.unity.playback-item";
- public SoundMenu (string? settings_action, DisplayFlags flags, string default_player_id) {
+ public SoundMenu (string? settings_action, DisplayFlags flags) {
/* A sound menu always has at least two sections: the volume section (this.volume_section)
* at the start of the menu, and the settings section at the end. Between those two,
* it has a dynamic amount of player sections, one for each registered player.
@@ -83,9 +83,6 @@ public class SoundMenu: Object
this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal);
this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0;
-
- this.default_player = default_player_id;
-
}
~SoundMenu () {
@@ -95,6 +92,16 @@ public class SoundMenu: Object
}
}
+ public void set_default_player (string default_player_id) {
+ this.default_player = default_player_id;
+ foreach (var player_stored in notify_handlers.get_keys ()) {
+ int index = this.find_player_section(player_stored);
+ if (index != -1 && player_stored.id == this.default_player) {
+ add_player_playback_controls (player_stored, index, true);
+ }
+ }
+ }
+
DBusConnection? bus = null;
uint export_id = 0;
@@ -171,7 +178,17 @@ public class SoundMenu: Object
}
}
}
-
+
+ private void check_last_running_player () {
+ foreach (var player in notify_handlers.get_keys ()) {
+ if (player.is_running && number_of_running_players == 1) {
+ // this is the first or the last player running...
+ // store its id
+ this.last_player_updated (player.id);
+ }
+ }
+ }
+
public void add_player (MediaPlayer player) {
if (this.notify_handlers.contains (player))
return;
@@ -198,11 +215,15 @@ public class SoundMenu: Object
// we need to update the rest of players, because we might have
// a non running player still showing the playback controls
update_all_players_play_section();
+
+ check_last_running_player ();
});
this.notify_handlers.insert (player, handler_id);
player.playlists_changed.connect (this.update_playlists);
player.playbackstatus_changed.connect (this.update_playbackstatus);
+
+ check_last_running_player ();
}
public void remove_player (MediaPlayer player) {
@@ -217,6 +238,8 @@ public class SoundMenu: Object
/* this'll drop our ref to it */
this.notify_handlers.remove (player);
+
+ check_last_running_player ();
}
public void update_volume_slider (VolumeControl.ActiveOutput active_output) {
@@ -368,16 +391,11 @@ public class SoundMenu: Object
this.menu.remove (index);
}
- void update_player_section (MediaPlayer player, int index) {
+ void add_player_playback_controls (MediaPlayer player, int index, bool adding_default_player) {
var player_section = this.menu.get_item_link(index, Menu.LINK_SECTION) as Menu;
int play_control_index = find_player_playback_controls_section (player_section);
- if (player.is_running && number_of_running_players == 1) {
- // this is the first or the last player running...
- // store its id
- this.last_player_updated (player.id);
- }
- if (player.is_running || !this.hide_inactive_player_controls) {
+ if (player.is_running || !this.hide_inactive_player_controls || (number_of_running_players == 0 && adding_default_player) ) {
MenuItem playback_item = create_playback_menu_item (player);
if (play_control_index != -1) {
player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS);
@@ -389,7 +407,11 @@ public class SoundMenu: Object
player_section.remove (PlayerSectionPosition.PLAYLIST);
player_section.remove (PlayerSectionPosition.PLAYER_CONTROLS);
}
- }
+ }
+ }
+
+ void update_player_section (MediaPlayer player, int index) {
+ add_player_playback_controls (player, index, false);
}
void update_playlists (MediaPlayer player) {
diff --git a/src/volume-control-pulse.vala b/src/volume-control-pulse.vala
index 6021447..c8a6071 100644
--- a/src/volume-control-pulse.vala
+++ b/src/volume-control-pulse.vala
@@ -22,13 +22,6 @@ using PulseAudio;
using Notify;
using Gee;
-[DBus (name="com.canonical.UnityGreeter.List")]
-interface GreeterListInterface : Object
-{
- public abstract async string get_active_entry () throws IOError;
- public signal void entry_selected (string entry_name);
-}
-
public class VolumeControlPulse : VolumeControl
{
private unowned PulseAudio.GLibMainLoop loop = null;
@@ -55,20 +48,17 @@ public class VolumeControlPulse : VolumeControl
private string? _objp_role_phone = null;
private uint _pa_volume_sig_count = 0;
- private DBusProxy _user_proxy;
- private GreeterListInterface _greeter_proxy;
- private Cancellable _mute_cancellable;
- private Cancellable _volume_cancellable;
private uint _local_volume_timer = 0;
private uint _accountservice_volume_timer = 0;
private bool _send_next_local_volume = false;
private double _account_service_volume = 0.0;
private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS;
+ private AccountsServiceAccess _accounts_service_access;
/** true when a microphone is active **/
public override bool active_mic { get; private set; default = false; }
- public VolumeControlPulse (IndicatorSound.Options options, PulseAudio.GLibMainLoop loop)
+ public VolumeControlPulse (IndicatorSound.Options options, PulseAudio.GLibMainLoop loop, AccountsServiceAccess? accounts_service_access)
{
base(options);
@@ -77,11 +67,14 @@ public class VolumeControlPulse : VolumeControl
this.loop = loop;
- _mute_cancellable = new Cancellable ();
- _volume_cancellable = new Cancellable ();
-
- setup_accountsservice.begin ();
-
+ _accounts_service_access = accounts_service_access;
+ this._accounts_service_access.notify["volume"].connect(() => {
+ if (this._accounts_service_access.volume >= 0 && _account_service_volume != this._accounts_service_access.volume) {
+ _account_service_volume = this._accounts_service_access.volume;
+ // we need to wait for this to settle.
+ start_account_service_volume_timer();
+ }
+ });
this.reconnect_to_pulse ();
}
@@ -537,7 +530,7 @@ public class VolumeControlPulse : VolumeControl
public override void set_mute (bool mute)
{
if (set_mute_internal (mute))
- sync_mute_to_accountsservice.begin (mute);
+ _accounts_service_access.mute = mute;
}
public void toggle_mute ()
@@ -773,128 +766,6 @@ public class VolumeControlPulse : VolumeControl
}
/* AccountsService operations */
- private void accountsservice_props_changed_cb (DBusProxy proxy, Variant changed_properties, string[]? invalidated_properties)
- {
- Variant volume_variant = changed_properties.lookup_value ("Volume", VariantType.DOUBLE);
- if (volume_variant != null) {
- var volume = volume_variant.get_double ();
- if (volume >= 0) {
- _account_service_volume = volume;
- // we need to wait for this to settle.
- start_account_service_volume_timer();
- }
- }
-
- Variant mute_variant = changed_properties.lookup_value ("Muted", VariantType.BOOLEAN);
- if (mute_variant != null) {
- var mute = mute_variant.get_boolean ();
- set_mute_internal (mute);
- }
- }
-
- private async void setup_user_proxy (string? username_in = null)
- {
- var username = username_in;
- _user_proxy = null;
-
- // Look up currently selected greeter user, if asked
- if (username == null) {
- try {
- username = yield _greeter_proxy.get_active_entry ();
- if (username == "" || username == null)
- return;
- } catch (GLib.Error e) {
- warning ("unable to find Accounts path for user %s: %s", username, e.message);
- return;
- }
- }
-
- // Get master AccountsService object
- DBusProxy accounts_proxy;
- try {
- accounts_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, null, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts");
- } catch (GLib.Error e) {
- warning ("unable to get greeter proxy: %s", e.message);
- return;
- }
-
- // Find user's AccountsService object
- try {
- var user_path_variant = yield accounts_proxy.call ("FindUserByName", new Variant ("(s)", username), DBusCallFlags.NONE, -1);
- string user_path;
- user_path_variant.get ("(o)", out user_path);
- _user_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, "org.freedesktop.Accounts", user_path, "com.ubuntu.AccountsService.Sound");
- } catch (GLib.Error e) {
- warning ("unable to find Accounts path for user %s: %s", username, e.message);
- return;
- }
-
- // Get current values and listen for changes
- _user_proxy.g_properties_changed.connect (accountsservice_props_changed_cb);
- try {
- var props_variant = yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "GetAll", new Variant ("(s)", _user_proxy.get_interface_name ()), null, DBusCallFlags.NONE, -1);
- Variant props;
- props_variant.get ("(@a{sv})", out props);
- accountsservice_props_changed_cb(_user_proxy, props, null);
- } catch (GLib.Error e) {
- debug("Unable to get properties for user %s at first try: %s", username, e.message);
- }
- }
-
- private void greeter_user_changed (string username)
- {
- setup_user_proxy.begin (username);
- }
-
- private async void setup_accountsservice ()
- {
- if (Environment.get_variable ("XDG_SESSION_CLASS") == "greeter") {
- try {
- _greeter_proxy = yield Bus.get_proxy (BusType.SESSION, "com.canonical.UnityGreeter", "/list");
- } catch (GLib.Error e) {
- warning ("unable to get greeter proxy: %s", e.message);
- return;
- }
- _greeter_proxy.entry_selected.connect (greeter_user_changed);
- yield setup_user_proxy ();
- } else {
- // We are in a user session. We just need our own proxy
- unowned string username = Environment.get_variable ("USER");
- if (username != "" && username != null) {
- yield setup_user_proxy (username);
- }
- }
- }
-
- private async void sync_mute_to_accountsservice (bool mute)
- {
- if (_user_proxy == null)
- return;
-
- _mute_cancellable.cancel ();
- _mute_cancellable.reset ();
-
- try {
- yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Muted", new Variant ("b", mute)), null, DBusCallFlags.NONE, -1, _mute_cancellable);
- } catch (GLib.Error e) {
- warning ("unable to sync mute to AccountsService: %s", e.message);
- }
- }
-
- private async void sync_volume_to_accountsservice (VolumeControl.Volume volume)
- {
- if (_user_proxy == null)
- return;
-
- _volume_cancellable.cancel ();
- _volume_cancellable.reset ();
-
- try {
- yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Volume", new Variant ("d", volume.volume)), null, DBusCallFlags.NONE, -1, _volume_cancellable);
- } catch (GLib.Error e) {
- warning ("unable to sync volume to AccountsService: %s", e.message);
- }
- }
private void start_local_volume_timer()
{
@@ -904,7 +775,7 @@ public class VolumeControlPulse : VolumeControl
stop_account_service_volume_timer();
if (_local_volume_timer == 0) {
- sync_volume_to_accountsservice.begin (_volume);
+ _accounts_service_access.volume = _volume.volume;
_local_volume_timer = Timeout.add_seconds (1, local_volume_changed_timeout);
} else {
_send_next_local_volume = true;
diff --git a/tests/integration/indicator-sound-test-base.cpp b/tests/integration/indicator-sound-test-base.cpp
index f61857e..cf60868 100644
--- a/tests/integration/indicator-sound-test-base.cpp
+++ b/tests/integration/indicator-sound-test-base.cpp
@@ -448,13 +448,37 @@ bool IndicatorSoundTestBase::initializeMenuChangedSignal()
return true;
}
-bool IndicatorSoundTestBase::waitVolumeChangedInIndicator()
+QVariant IndicatorSoundTestBase::waitPropertyChanged(QSignalSpy *signalSpy, QString property)
{
- if (signal_spy_volume_changed_)
+ QVariant ret_val;
+ if (signalSpy)
{
- return signal_spy_volume_changed_->wait();
+ signalSpy->wait();
+ if (signalSpy->count())
+ {
+ QList<QVariant> arguments = signalSpy->takeLast();
+ if (arguments.size() == 3 && static_cast<QMetaType::Type>(arguments.at(1).type()) == QMetaType::QVariantMap)
+ {
+ QMap<QString, QVariant> map = arguments.at(1).toMap();
+ QMap<QString, QVariant>::iterator iter = map.find(property);
+ if (iter != map.end())
+ {
+ return iter.value();
+ }
+ }
+ }
}
- return false;
+ return ret_val;
+}
+bool IndicatorSoundTestBase::waitVolumeChangedInIndicator()
+{
+ QVariant val = waitPropertyChanged(signal_spy_volume_changed_.get(), "Volume");
+ return !val.isNull();
+}
+
+QVariant IndicatorSoundTestBase::waitLastRunningPlayerChanged()
+{
+ return waitPropertyChanged(signal_spy_volume_changed_.get(), "LastRunningPlayer");
}
void IndicatorSoundTestBase::initializeAccountsInterface()
diff --git a/tests/integration/indicator-sound-test-base.h b/tests/integration/indicator-sound-test-base.h
index 969fd69..5c36031 100644
--- a/tests/integration/indicator-sound-test-base.h
+++ b/tests/integration/indicator-sound-test-base.h
@@ -144,6 +144,10 @@ protected:
float getVolumeValue(bool *isValid = nullptr);
+ static QVariant waitPropertyChanged(QSignalSpy * signalSpy, QString property);
+
+ QVariant waitLastRunningPlayerChanged();
+
QtDBusTest::DBusTestRunner dbusTestRunner;
QtDBusMock::DBusMock dbusMock;
diff --git a/tests/integration/test-indicator.cpp b/tests/integration/test-indicator.cpp
index 33a27af..bc426aa 100644
--- a/tests/integration/test-indicator.cpp
+++ b/tests/integration/test-indicator.cpp
@@ -394,9 +394,7 @@ TEST_F(TestIndicator, DesktopAddMprisPlayer)
.string_attribute("x-canonical-type", "com.canonical.unity.media-player")
)
.item(mh::MenuItemMatcher()
- .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
.string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
- .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop")
.string_attribute("x-canonical-type","com.canonical.unity.playback-item")
)
)
@@ -925,9 +923,7 @@ TEST_F(TestIndicator, DesktopMprisPlayersPlaybackControls)
.string_attribute("x-canonical-type", "com.canonical.unity.media-player")
)
.item(mh::MenuItemMatcher()
- .string_attribute("x-canonical-previous-action","indicator.previous.testplayer3.desktop")
.string_attribute("x-canonical-play-action","indicator.play.testplayer3.desktop")
- .string_attribute("x-canonical-next-action","indicator.next.testplayer3.desktop")
.string_attribute("x-canonical-type","com.canonical.unity.playback-item")
)
)
@@ -935,6 +931,67 @@ TEST_F(TestIndicator, DesktopMprisPlayersPlaybackControls)
.label("Sound Settings…")
)
).match());
+
+ // check that the last running player is the one we expect
+ QVariant lastPlayerRunning = waitLastRunningPlayerChanged();
+ EXPECT_TRUE(lastPlayerRunning.type() == QVariant::String);
+ EXPECT_EQ(lastPlayerRunning.toString(), "testplayer3.desktop");
+
+ // restart the indicator to simulate a new user session
+ ASSERT_NO_THROW(startIndicator());
+
+ // check that player 3 is the only one with playback controls
+ // as it was the last one being stopped
+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
+ .item(mh::MenuItemMatcher()
+ .action("indicator.root")
+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
+ .mode(mh::MenuItemMatcher::Mode::all)
+ .submenu()
+ .item(mh::MenuItemMatcher()
+ .section()
+ .item(mh::MenuItemMatcher().checkbox()
+ .label("Mute")
+ )
+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
+ )
+ .item(mh::MenuItemMatcher()
+ .section()
+ .item(mh::MenuItemMatcher()
+ .action("indicator.testplayer3.desktop")
+ .label("TestPlayer3")
+ .themed_icon("icon", {"testplayer"})
+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
+ )
+ .item(mh::MenuItemMatcher()
+ .string_attribute("x-canonical-play-action","indicator.play.testplayer3.desktop")
+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
+ )
+ )
+ .item(mh::MenuItemMatcher()
+ .section()
+ .item(mh::MenuItemMatcher()
+ .action("indicator.testplayer1.desktop")
+ .label("TestPlayer1")
+ .themed_icon("icon", {"testplayer"})
+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
+ )
+ )
+ .item(mh::MenuItemMatcher()
+ .section()
+ .item(mh::MenuItemMatcher()
+ .action("indicator.testplayer2.desktop")
+ .label("TestPlayer2")
+ .themed_icon("icon", {"testplayer"})
+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
+ )
+ )
+ .item(mh::MenuItemMatcher()
+ .label("Sound Settings…")
+ )
+ ).match());
+
}
TEST_F(TestIndicator, DesktopMprisPlayerButtonsState)
diff --git a/tests/notifications-test.cc b/tests/notifications-test.cc
index 6f523f1..fb55f6a 100644
--- a/tests/notifications-test.cc
+++ b/tests/notifications-test.cc
@@ -36,592 +36,602 @@ extern "C" {
class NotificationsTest : public ::testing::Test
{
- protected:
- DbusTestService * service = NULL;
-
- GDBusConnection * session = NULL;
- std::shared_ptr<NotificationsMock> notifications;
-
- virtual void SetUp() {
- g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
- g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
-
- service = dbus_test_service_new(NULL);
- dbus_test_service_set_bus(service, DBUS_TEST_SERVICE_BUS_SESSION);
-
- /* Useful for debugging test failures, not needed all the time (until it fails) */
- #if 0
- auto bustle = std::shared_ptr<DbusTestTask>([]() {
- DbusTestTask * bustle = DBUS_TEST_TASK(dbus_test_bustle_new("notifications-test.bustle"));
- dbus_test_task_set_name(bustle, "Bustle");
- dbus_test_task_set_bus(bustle, DBUS_TEST_SERVICE_BUS_SESSION);
- return bustle;
- }(), [](DbusTestTask * bustle) {
- g_clear_object(&bustle);
- });
- dbus_test_service_add_task(service, bustle.get());
- #endif
-
- notifications = std::make_shared<NotificationsMock>();
-
- dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
- dbus_test_service_start_tasks(service);
-
- session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
- ASSERT_NE(nullptr, session);
- g_dbus_connection_set_exit_on_close(session, FALSE);
- g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session);
-
- /* This is done in main.c */
- notify_init("indicator-sound");
- }
-
- virtual void TearDown() {
- if (notify_is_initted())
- notify_uninit();
-
- notifications.reset();
- g_clear_object(&service);
-
- g_object_unref(session);
-
- unsigned int cleartry = 0;
- while (session != NULL && cleartry < 100) {
- loop(100);
- cleartry++;
- }
-
- ASSERT_EQ(nullptr, session);
- }
-
- static gboolean timeout_cb (gpointer user_data) {
- GMainLoop * loop = static_cast<GMainLoop *>(user_data);
- g_main_loop_quit(loop);
- return G_SOURCE_REMOVE;
- }
-
- void loop (unsigned int ms) {
- GMainLoop * loop = g_main_loop_new(NULL, FALSE);
- g_timeout_add(ms, timeout_cb, loop);
- g_main_loop_run(loop);
- g_main_loop_unref(loop);
- }
-
- void loop_until(const std::function<bool()>& test, unsigned int max_ms=50, unsigned int test_interval_ms=10) {
-
- // g_timeout's callback only allows a single pointer,
- // so use a temporary stack struct to wedge everything into one pointer
- struct CallbackData {
- const std::function<bool()>& test;
- const gint64 deadline;
- GMainLoop* loop = g_main_loop_new(nullptr, false);
- CallbackData (const std::function<bool()>& f, unsigned int max_ms):
- test{f},
- deadline{g_get_monotonic_time() + (max_ms*1000)} {}
- ~CallbackData() {g_main_loop_unref(loop);}
- } data(test, max_ms);
-
- // tell the timer to stop looping on success or deadline
- auto timerfunc = [](gpointer gdata) -> gboolean {
- auto& data = *static_cast<CallbackData*>(gdata);
- if (!data.test() && (g_get_monotonic_time() < data.deadline))
- return G_SOURCE_CONTINUE;
- g_main_loop_quit(data.loop);
- return G_SOURCE_REMOVE;
- };
-
- // start looping
- g_timeout_add (std::min(max_ms, test_interval_ms), timerfunc, &data);
- g_main_loop_run(data.loop);
- }
-
- void loop_until_notifications(unsigned int max_seconds=1) {
- auto test = [this]{ return !notifications->getNotifications().empty(); };
- loop_until(test, max_seconds);
- }
-
- static int unref_idle (gpointer user_data) {
- g_variant_unref(static_cast<GVariant *>(user_data));
- return G_SOURCE_REMOVE;
- }
-
- std::shared_ptr<MediaPlayerList> playerListMock () {
- auto playerList = std::shared_ptr<MediaPlayerList>(
- MEDIA_PLAYER_LIST(media_player_list_mock_new()),
- [](MediaPlayerList * list) {
- g_clear_object(&list);
- });
- return playerList;
- }
-
- std::shared_ptr<IndicatorSoundOptions> optionsMock () {
- auto options = std::shared_ptr<IndicatorSoundOptions>(
- INDICATOR_SOUND_OPTIONS(options_mock_new()),
- [](IndicatorSoundOptions * options){
- g_clear_object(&options);
- });
- return options;
- }
-
- std::shared_ptr<VolumeControl> volumeControlMock (const std::shared_ptr<IndicatorSoundOptions>& optionsMock) {
- auto volumeControl = std::shared_ptr<VolumeControl>(
- VOLUME_CONTROL(volume_control_mock_new(optionsMock.get())),
- [](VolumeControl * control){
- g_clear_object(&control);
- });
- return volumeControl;
- }
-
- std::shared_ptr<VolumeWarning> volumeWarningMock (const std::shared_ptr<IndicatorSoundOptions>& optionsMock) {
- auto volumeWarning = std::shared_ptr<VolumeWarning>(
- VOLUME_WARNING(volume_warning_mock_new(optionsMock.get())),
- [](VolumeWarning * warning){
- g_clear_object(&warning);
- });
- return volumeWarning;
- }
-
- std::shared_ptr<IndicatorSoundService> standardService (
- const std::shared_ptr<VolumeControl>& volumeControl,
- const std::shared_ptr<MediaPlayerList>& playerList,
- const std::shared_ptr<IndicatorSoundOptions>& options,
- const std::shared_ptr<VolumeWarning>& warning) {
- auto soundService = std::shared_ptr<IndicatorSoundService>(
- indicator_sound_service_new(playerList.get(), volumeControl.get(), nullptr, options.get(), warning.get()),
- [](IndicatorSoundService * service){
- g_clear_object(&service);
- });
-
- return soundService;
- }
-
- void setMockVolume (std::shared_ptr<VolumeControl> volumeControl, double volume, VolumeControlVolumeReasons reason = VOLUME_CONTROL_VOLUME_REASONS_USER_KEYPRESS) {
- VolumeControlVolume * vol = volume_control_volume_new();
- vol->volume = volume;
- vol->reason = reason;
-
- volume_control_set_volume(volumeControl.get(), vol);
- g_object_unref(vol);
- }
-
- void setIndicatorShown (bool shown) {
- auto bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
-
- g_dbus_connection_call(bus,
- g_dbus_connection_get_unique_name(bus),
- "/com/canonical/indicator/sound",
- "org.gtk.Actions",
- "SetState",
- g_variant_new("(sva{sv})", "indicator-shown", g_variant_new_boolean(shown), nullptr),
- nullptr,
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- nullptr,
- nullptr,
- nullptr);
-
- g_clear_object(&bus);
- }
+ protected:
+ DbusTestService * service = NULL;
+
+ GDBusConnection * session = NULL;
+ std::shared_ptr<NotificationsMock> notifications;
+
+ virtual void SetUp() {
+ g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
+ g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
+
+ service = dbus_test_service_new(NULL);
+ dbus_test_service_set_bus(service, DBUS_TEST_SERVICE_BUS_SESSION);
+
+ /* Useful for debugging test failures, not needed all the time (until it fails) */
+ #if 0
+ auto bustle = std::shared_ptr<DbusTestTask>([]() {
+ DbusTestTask * bustle = DBUS_TEST_TASK(dbus_test_bustle_new("notifications-test.bustle"));
+ dbus_test_task_set_name(bustle, "Bustle");
+ dbus_test_task_set_bus(bustle, DBUS_TEST_SERVICE_BUS_SESSION);
+ return bustle;
+ }(), [](DbusTestTask * bustle) {
+ g_clear_object(&bustle);
+ });
+ dbus_test_service_add_task(service, bustle.get());
+ #endif
+
+ notifications = std::make_shared<NotificationsMock>();
+
+ dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
+ dbus_test_service_start_tasks(service);
+
+ session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
+ ASSERT_NE(nullptr, session);
+ g_dbus_connection_set_exit_on_close(session, FALSE);
+ g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session);
+
+ /* This is done in main.c */
+ notify_init("indicator-sound");
+ }
+
+ virtual void TearDown() {
+ if (notify_is_initted())
+ notify_uninit();
+
+ notifications.reset();
+ g_clear_object(&service);
+
+ g_object_unref(session);
+
+ unsigned int cleartry = 0;
+ while (session != NULL && cleartry < 100) {
+ loop(100);
+ cleartry++;
+ }
+
+ ASSERT_EQ(nullptr, session);
+ }
+
+ static gboolean timeout_cb (gpointer user_data) {
+ GMainLoop * loop = static_cast<GMainLoop *>(user_data);
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ }
+
+ void loop (unsigned int ms) {
+ GMainLoop * loop = g_main_loop_new(NULL, FALSE);
+ g_timeout_add(ms, timeout_cb, loop);
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+ }
+
+ void loop_until(const std::function<bool()>& test, unsigned int max_ms=50, unsigned int test_interval_ms=10) {
+
+ // g_timeout's callback only allows a single pointer,
+ // so use a temporary stack struct to wedge everything into one pointer
+ struct CallbackData {
+ const std::function<bool()>& test;
+ const gint64 deadline;
+ GMainLoop* loop = g_main_loop_new(nullptr, false);
+ CallbackData (const std::function<bool()>& f, unsigned int max_ms):
+ test{f},
+ deadline{g_get_monotonic_time() + (max_ms*1000)} {}
+ ~CallbackData() {g_main_loop_unref(loop);}
+ } data(test, max_ms);
+
+ // tell the timer to stop looping on success or deadline
+ auto timerfunc = [](gpointer gdata) -> gboolean {
+ auto& data = *static_cast<CallbackData*>(gdata);
+ if (!data.test() && (g_get_monotonic_time() < data.deadline))
+ return G_SOURCE_CONTINUE;
+ g_main_loop_quit(data.loop);
+ return G_SOURCE_REMOVE;
+ };
+
+ // start looping
+ g_timeout_add (std::min(max_ms, test_interval_ms), timerfunc, &data);
+ g_main_loop_run(data.loop);
+ }
+
+ void loop_until_notifications(unsigned int max_seconds=1) {
+ auto test = [this]{ return !notifications->getNotifications().empty(); };
+ loop_until(test, max_seconds);
+ }
+
+ static int unref_idle (gpointer user_data) {
+ g_variant_unref(static_cast<GVariant *>(user_data));
+ return G_SOURCE_REMOVE;
+ }
+
+ std::shared_ptr<MediaPlayerList> playerListMock () {
+ auto playerList = std::shared_ptr<MediaPlayerList>(
+ MEDIA_PLAYER_LIST(media_player_list_mock_new()),
+ [](MediaPlayerList * list) {
+ g_clear_object(&list);
+ });
+ return playerList;
+ }
+
+ std::shared_ptr<IndicatorSoundOptions> optionsMock () {
+ auto options = std::shared_ptr<IndicatorSoundOptions>(
+ INDICATOR_SOUND_OPTIONS(options_mock_new()),
+ [](IndicatorSoundOptions * options){
+ g_clear_object(&options);
+ });
+ return options;
+ }
+
+ std::shared_ptr<VolumeControl> volumeControlMock (const std::shared_ptr<IndicatorSoundOptions>& optionsMock) {
+ auto volumeControl = std::shared_ptr<VolumeControl>(
+ VOLUME_CONTROL(volume_control_mock_new(optionsMock.get())),
+ [](VolumeControl * control){
+ g_clear_object(&control);
+ });
+ return volumeControl;
+ }
+
+ std::shared_ptr<VolumeWarning> volumeWarningMock (const std::shared_ptr<IndicatorSoundOptions>& optionsMock) {
+ auto volumeWarning = std::shared_ptr<VolumeWarning>(
+ VOLUME_WARNING(volume_warning_mock_new(optionsMock.get())),
+ [](VolumeWarning * warning){
+ g_clear_object(&warning);
+ });
+ return volumeWarning;
+ }
+
+ std::shared_ptr<IndicatorSoundService> standardService (
+ const std::shared_ptr<VolumeControl>& volumeControl,
+ const std::shared_ptr<MediaPlayerList>& playerList,
+ const std::shared_ptr<IndicatorSoundOptions>& options,
+ const std::shared_ptr<VolumeWarning>& warning,
+ const std::shared_ptr<AccountsServiceAccess>& accounts_service_access) {
+ auto soundService = std::shared_ptr<IndicatorSoundService>(
+ indicator_sound_service_new(playerList.get(), volumeControl.get(), nullptr, options.get(), warning.get(), accounts_service_access.get()),
+ [](IndicatorSoundService * service){
+ g_clear_object(&service);
+ });
+
+ return soundService;
+ }
+
+ void setMockVolume (std::shared_ptr<VolumeControl> volumeControl, double volume, VolumeControlVolumeReasons reason = VOLUME_CONTROL_VOLUME_REASONS_USER_KEYPRESS) {
+ VolumeControlVolume * vol = volume_control_volume_new();
+ vol->volume = volume;
+ vol->reason = reason;
+
+ volume_control_set_volume(volumeControl.get(), vol);
+ g_object_unref(vol);
+ }
+
+ void setIndicatorShown (bool shown) {
+ auto bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
+
+ g_dbus_connection_call(bus,
+ g_dbus_connection_get_unique_name(bus),
+ "/com/canonical/indicator/sound",
+ "org.gtk.Actions",
+ "SetState",
+ g_variant_new("(sva{sv})", "indicator-shown", g_variant_new_boolean(shown), nullptr),
+ nullptr,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ nullptr,
+ nullptr,
+ nullptr);
+
+ g_clear_object(&bus);
+ }
};
TEST_F(NotificationsTest, BasicObject) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
- /* Give some time settle */
- loop(50);
+ /* Give some time settle */
+ loop(50);
- /* Auto free */
+ /* Auto free */
}
TEST_F(NotificationsTest, VolumeChanges) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.50);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_EQ("indicator-sound", notev[0].app_name);
- EXPECT_EQ("Volume", notev[0].summary);
- EXPECT_EQ(0, notev[0].actions.size());
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
- EXPECT_GVARIANT_EQ("@i 50", notev[0].hints["value"]);
-
- /* Set a different volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.60);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@i 60", notev[0].hints["value"]);
-
- /* Have pulse set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.70, VOLUME_CONTROL_VOLUME_REASONS_PULSE_CHANGE);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(0, notev.size());
-
- /* Have AS set the volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.80, VOLUME_CONTROL_VOLUME_REASONS_ACCOUNTS_SERVICE_SET);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(0, notev.size());
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_EQ("indicator-sound", notev[0].app_name);
+ EXPECT_EQ("Volume", notev[0].summary);
+ EXPECT_EQ(0, notev[0].actions.size());
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
+ EXPECT_GVARIANT_EQ("@i 50", notev[0].hints["value"]);
+
+ /* Set a different volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.60);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@i 60", notev[0].hints["value"]);
+
+ /* Have pulse set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.70, VOLUME_CONTROL_VOLUME_REASONS_PULSE_CHANGE);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(0, notev.size());
+
+ /* Have AS set the volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.80, VOLUME_CONTROL_VOLUME_REASONS_ACCOUNTS_SERVICE_SET);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(0, notev.size());
}
TEST_F(NotificationsTest, StreamChanges) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.5);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
-
- /* Change Streams, no volume change */
- notifications->clearNotifications();
- volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALARM);
- setMockVolume(volumeControl, 0.5, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
- loop(50);
- notev = notifications->getNotifications();
- EXPECT_EQ(0, notev.size());
-
- /* Change Streams, volume change */
- notifications->clearNotifications();
- volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALERT);
- setMockVolume(volumeControl, 0.6, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
- loop(50);
- notev = notifications->getNotifications();
- EXPECT_EQ(0, notev.size());
-
- /* Change Streams, no volume change, volume up */
- notifications->clearNotifications();
- volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_MULTIMEDIA);
- setMockVolume(volumeControl, 0.6, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
- loop(50);
- setMockVolume(volumeControl, 0.65);
- notev = notifications->getNotifications();
- EXPECT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@i 65", notev[0].hints["value"]);
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.5);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+
+ /* Change Streams, no volume change */
+ notifications->clearNotifications();
+ volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALARM);
+ setMockVolume(volumeControl, 0.5, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
+ loop(50);
+ notev = notifications->getNotifications();
+ EXPECT_EQ(0, notev.size());
+
+ /* Change Streams, volume change */
+ notifications->clearNotifications();
+ volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALERT);
+ setMockVolume(volumeControl, 0.6, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
+ loop(50);
+ notev = notifications->getNotifications();
+ EXPECT_EQ(0, notev.size());
+
+ /* Change Streams, no volume change, volume up */
+ notifications->clearNotifications();
+ volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_MULTIMEDIA);
+ setMockVolume(volumeControl, 0.6, VOLUME_CONTROL_VOLUME_REASONS_VOLUME_STREAM_CHANGE);
+ loop(50);
+ setMockVolume(volumeControl, 0.65);
+ notev = notifications->getNotifications();
+ EXPECT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@i 65", notev[0].hints["value"]);
}
TEST_F(NotificationsTest, IconTesting) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set an initial volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.5);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
-
- /* Generate a set of notifications */
- notifications->clearNotifications();
- for (float i = 0.0; i < 1.01; i += 0.1) {
- setMockVolume(volumeControl, i);
- }
-
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(11, notev.size());
-
- EXPECT_EQ("audio-volume-muted", notev[0].app_icon);
- EXPECT_EQ("audio-volume-low", notev[1].app_icon);
- EXPECT_EQ("audio-volume-low", notev[2].app_icon);
- EXPECT_EQ("audio-volume-medium", notev[3].app_icon);
- EXPECT_EQ("audio-volume-medium", notev[4].app_icon);
- EXPECT_EQ("audio-volume-medium", notev[5].app_icon);
- EXPECT_EQ("audio-volume-medium", notev[6].app_icon);
- EXPECT_EQ("audio-volume-high", notev[7].app_icon);
- EXPECT_EQ("audio-volume-high", notev[8].app_icon);
- EXPECT_EQ("audio-volume-high", notev[9].app_icon);
- EXPECT_EQ("audio-volume-high", notev[10].app_icon);
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set an initial volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.5);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+
+ /* Generate a set of notifications */
+ notifications->clearNotifications();
+ for (float i = 0.0; i < 1.01; i += 0.1) {
+ setMockVolume(volumeControl, i);
+ }
+
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(11, notev.size());
+
+ EXPECT_EQ("audio-volume-muted", notev[0].app_icon);
+ EXPECT_EQ("audio-volume-low", notev[1].app_icon);
+ EXPECT_EQ("audio-volume-low", notev[2].app_icon);
+ EXPECT_EQ("audio-volume-medium", notev[3].app_icon);
+ EXPECT_EQ("audio-volume-medium", notev[4].app_icon);
+ EXPECT_EQ("audio-volume-medium", notev[5].app_icon);
+ EXPECT_EQ("audio-volume-medium", notev[6].app_icon);
+ EXPECT_EQ("audio-volume-high", notev[7].app_icon);
+ EXPECT_EQ("audio-volume-high", notev[8].app_icon);
+ EXPECT_EQ("audio-volume-high", notev[9].app_icon);
+ EXPECT_EQ("audio-volume-high", notev[10].app_icon);
}
TEST_F(NotificationsTest, ServerRestart) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.50);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
-
- /* Restart server without sync notifications */
- notifications->clearNotifications();
- dbus_test_service_remove_task(service, (DbusTestTask*)*notifications);
- notifications.reset();
-
- loop(50);
-
- notifications = std::make_shared<NotificationsMock>(std::vector<std::string>({"body", "body-markup", "icon-static"}));
- dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
- dbus_test_task_run((DbusTestTask*)*notifications);
-
- /* Change the volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.60);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(0, notev.size());
-
- /* Put a good server back */
- dbus_test_service_remove_task(service, (DbusTestTask*)*notifications);
- notifications.reset();
-
- loop(50);
-
- notifications = std::make_shared<NotificationsMock>();
- dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
- dbus_test_task_run((DbusTestTask*)*notifications);
-
- /* Change the volume again */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.70);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+
+ /* Restart server without sync notifications */
+ notifications->clearNotifications();
+ dbus_test_service_remove_task(service, (DbusTestTask*)*notifications);
+ notifications.reset();
+
+ loop(50);
+
+ notifications = std::make_shared<NotificationsMock>(std::vector<std::string>({"body", "body-markup", "icon-static"}));
+ dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
+ dbus_test_task_run((DbusTestTask*)*notifications);
+
+ /* Change the volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.60);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(0, notev.size());
+
+ /* Put a good server back */
+ dbus_test_service_remove_task(service, (DbusTestTask*)*notifications);
+ notifications.reset();
+
+ loop(50);
+
+ notifications = std::make_shared<NotificationsMock>();
+ dbus_test_service_add_task(service, (DbusTestTask*)*notifications);
+ dbus_test_task_run((DbusTestTask*)*notifications);
+
+ /* Change the volume again */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.70);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
}
TEST_F(NotificationsTest, HighVolume) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.50);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_EQ("Volume", notev[0].summary);
- EXPECT_EQ("Speakers", notev[0].body);
- EXPECT_GVARIANT_EQ("@s 'false'", notev[0].hints["x-canonical-value-bar-tint"]);
-
- /* Set high volume with volume change */
- notifications->clearNotifications();
- volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), true);
- setMockVolume(volumeControl, 0.90);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_LT(0, notev.size()); /* This passes with one or two since it would just be an update to the first if a second was sent */
- EXPECT_EQ("Volume", notev[0].summary);
- EXPECT_EQ("Speakers", notev[0].body);
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]);
-
- /* Move it back */
- volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), false);
- setMockVolume(volumeControl, 0.50);
- loop(50);
-
- /* Set high volume without level change */
- /* NOTE: This can happen if headphones are plugged in */
- notifications->clearNotifications();
- volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), true);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_EQ("Volume", notev[0].summary);
- EXPECT_EQ("Speakers", notev[0].body);
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]);
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_EQ("Volume", notev[0].summary);
+ EXPECT_EQ("Speakers", notev[0].body);
+ EXPECT_GVARIANT_EQ("@s 'false'", notev[0].hints["x-canonical-value-bar-tint"]);
+
+ /* Set high volume with volume change */
+ notifications->clearNotifications();
+ volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), true);
+ setMockVolume(volumeControl, 0.90);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_LT(0, notev.size()); /* This passes with one or two since it would just be an update to the first if a second was sent */
+ EXPECT_EQ("Volume", notev[0].summary);
+ EXPECT_EQ("Speakers", notev[0].body);
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]);
+
+ /* Move it back */
+ volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), false);
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+
+ /* Set high volume without level change */
+ /* NOTE: This can happen if headphones are plugged in */
+ notifications->clearNotifications();
+ volume_warning_mock_set_high_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), true);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_EQ("Volume", notev[0].summary);
+ EXPECT_EQ("Speakers", notev[0].body);
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]);
}
TEST_F(NotificationsTest, MenuHide) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.50);
- loop(50);
- auto notev = notifications->getNotifications();
- EXPECT_EQ(1, notev.size());
-
- /* Set the indicator to shown, and set a new volume */
- notifications->clearNotifications();
- setIndicatorShown(true);
- loop(50);
- setMockVolume(volumeControl, 0.60);
- loop(50);
- notev = notifications->getNotifications();
- EXPECT_EQ(0, notev.size());
-
- /* Set the indicator to hidden, and set a new volume */
- notifications->clearNotifications();
- setIndicatorShown(false);
- loop(50);
- setMockVolume(volumeControl, 0.70);
- loop(50);
- notev = notifications->getNotifications();
- EXPECT_EQ(1, notev.size());
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ EXPECT_EQ(1, notev.size());
+
+ /* Set the indicator to shown, and set a new volume */
+ notifications->clearNotifications();
+ setIndicatorShown(true);
+ loop(50);
+ setMockVolume(volumeControl, 0.60);
+ loop(50);
+ notev = notifications->getNotifications();
+ EXPECT_EQ(0, notev.size());
+
+ /* Set the indicator to hidden, and set a new volume */
+ notifications->clearNotifications();
+ setIndicatorShown(false);
+ loop(50);
+ setMockVolume(volumeControl, 0.70);
+ loop(50);
+ notev = notifications->getNotifications();
+ EXPECT_EQ(1, notev.size());
}
TEST_F(NotificationsTest, ExtendendVolumeNotification) {
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- /* Set a volume */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 0.50);
- loop(50);
- auto notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_EQ("indicator-sound", notev[0].app_name);
- EXPECT_EQ("Volume", notev[0].summary);
- EXPECT_EQ(0, notev[0].actions.size());
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
- EXPECT_GVARIANT_EQ("@i 50", notev[0].hints["value"]);
-
- /* Allow an amplified volume */
- notifications->clearNotifications();
- volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALARM);
- options_mock_mock_set_max_volume(OPTIONS_MOCK(options.get()), 1.5);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@i 33", notev[0].hints["value"]);
-
- /* Set to 'over max' */
- notifications->clearNotifications();
- setMockVolume(volumeControl, 1.525);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@i 100", notev[0].hints["value"]);
-
- /* Put back */
- notifications->clearNotifications();
- options_mock_mock_set_max_volume(OPTIONS_MOCK(options.get()), 1.0);
- loop(50);
- notev = notifications->getNotifications();
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@i 100", notev[0].hints["value"]);
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ /* Set a volume */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 0.50);
+ loop(50);
+ auto notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_EQ("indicator-sound", notev[0].app_name);
+ EXPECT_EQ("Volume", notev[0].summary);
+ EXPECT_EQ(0, notev[0].actions.size());
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
+ EXPECT_GVARIANT_EQ("@i 50", notev[0].hints["value"]);
+
+ /* Allow an amplified volume */
+ notifications->clearNotifications();
+ volume_control_mock_mock_set_active_stream(VOLUME_CONTROL_MOCK(volumeControl.get()), VOLUME_CONTROL_STREAM_ALARM);
+ options_mock_mock_set_max_volume(OPTIONS_MOCK(options.get()), 1.5);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@i 33", notev[0].hints["value"]);
+
+ /* Set to 'over max' */
+ notifications->clearNotifications();
+ setMockVolume(volumeControl, 1.525);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@i 100", notev[0].hints["value"]);
+
+ /* Put back */
+ notifications->clearNotifications();
+ options_mock_mock_set_max_volume(OPTIONS_MOCK(options.get()), 1.0);
+ loop(50);
+ notev = notifications->getNotifications();
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@i 100", notev[0].hints["value"]);
}
TEST_F(NotificationsTest, TriggerWarning) {
- // Tests all the conditions needed to trigger a volume warning.
- // There are many possible combinations, so this test is slow. :P
-
- const struct {
- bool expected;
- VolumeControlActiveOutput output;
- } test_outputs[] = {
- { false, VOLUME_CONTROL_ACTIVE_OUTPUT_SPEAKERS },
- { true, VOLUME_CONTROL_ACTIVE_OUTPUT_HEADPHONES },
- { true, VOLUME_CONTROL_ACTIVE_OUTPUT_BLUETOOTH_HEADPHONES },
- { false, VOLUME_CONTROL_ACTIVE_OUTPUT_BLUETOOTH_SPEAKER },
- { false, VOLUME_CONTROL_ACTIVE_OUTPUT_USB_SPEAKER },
- { true, VOLUME_CONTROL_ACTIVE_OUTPUT_USB_HEADPHONES },
- { false, VOLUME_CONTROL_ACTIVE_OUTPUT_HDMI_SPEAKER },
- { true, VOLUME_CONTROL_ACTIVE_OUTPUT_HDMI_HEADPHONES },
- { false, VOLUME_CONTROL_ACTIVE_OUTPUT_CALL_MODE }
- };
-
- const struct {
- bool expected;
- pa_volume_t volume;
- pa_volume_t loud_volume;
- } test_volumes[] = {
- { false, 50, 100 },
- { false, 99, 100 },
- { true, 100, 100 },
- { true, 101, 100 }
- };
-
- const struct {
- bool expected;
- bool approved;
- } test_approved[] = {
- { true, false },
- { false, true }
- };
-
- const struct {
- bool expected;
- bool warnings_enabled;
- } test_warnings_enabled[] = {
- { true, true },
- { false, false }
- };
-
- const struct {
- bool expected;
- bool multimedia_active;
- } test_multimedia_active[] = {
- { true, true },
- { false, false }
- };
-
- for (const auto& outputs : test_outputs) {
- for (const auto& volumes : test_volumes) {
- for (const auto& approved : test_approved) {
- for (const auto& warnings_enabled : test_warnings_enabled) {
- for (const auto& multimedia_active : test_multimedia_active) {
-
- notifications->clearNotifications();
-
- // instantiate the test subjects
- auto options = optionsMock();
- auto volumeControl = volumeControlMock(options);
- auto volumeWarning = volumeWarningMock(options);
- auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning);
-
- // run the test
- options_mock_mock_set_loud_volume(OPTIONS_MOCK(options.get()), volumes.loud_volume);
- options_mock_mock_set_loud_warning_enabled(OPTIONS_MOCK(options.get()), warnings_enabled.warnings_enabled);
- volume_warning_mock_set_approved(VOLUME_WARNING_MOCK(volumeWarning.get()), approved.approved);
- volume_warning_mock_set_multimedia_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), volumes.volume);
- volume_warning_mock_set_multimedia_active(VOLUME_WARNING_MOCK(volumeWarning.get()), multimedia_active.multimedia_active);
- volume_control_mock_mock_set_active_output(VOLUME_CONTROL_MOCK(volumeControl.get()), outputs.output);
-
- loop_until_notifications();
-
- // check the result
- auto notev = notifications->getNotifications();
- const bool warning_expected = outputs.expected && volumes.expected && approved.expected && warnings_enabled.expected && multimedia_active.expected;
- if (warning_expected) {
- EXPECT_TRUE(volume_warning_get_active(volumeWarning.get()));
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-snap-decisions"]);
- EXPECT_GVARIANT_EQ(nullptr, notev[0].hints["x-canonical-private-synchronous"]);
- }
- else {
- EXPECT_FALSE(volume_warning_get_active(volumeWarning.get()));
- ASSERT_EQ(1, notev.size());
- EXPECT_GVARIANT_EQ(nullptr, notev[0].hints["x-canonical-snap-decisions"]);
- EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
- }
-
- } // multimedia_active
- } // warnings_enabled
- } // approved
- } // volumes
- } // outputs
+ // Tests all the conditions needed to trigger a volume warning.
+ // There are many possible combinations, so this test is slow. :P
+
+ const struct {
+ bool expected;
+ VolumeControlActiveOutput output;
+ } test_outputs[] = {
+ { false, VOLUME_CONTROL_ACTIVE_OUTPUT_SPEAKERS },
+ { true, VOLUME_CONTROL_ACTIVE_OUTPUT_HEADPHONES },
+ { true, VOLUME_CONTROL_ACTIVE_OUTPUT_BLUETOOTH_HEADPHONES },
+ { false, VOLUME_CONTROL_ACTIVE_OUTPUT_BLUETOOTH_SPEAKER },
+ { false, VOLUME_CONTROL_ACTIVE_OUTPUT_USB_SPEAKER },
+ { true, VOLUME_CONTROL_ACTIVE_OUTPUT_USB_HEADPHONES },
+ { false, VOLUME_CONTROL_ACTIVE_OUTPUT_HDMI_SPEAKER },
+ { true, VOLUME_CONTROL_ACTIVE_OUTPUT_HDMI_HEADPHONES },
+ { false, VOLUME_CONTROL_ACTIVE_OUTPUT_CALL_MODE }
+ };
+
+ const struct {
+ bool expected;
+ pa_volume_t volume;
+ pa_volume_t loud_volume;
+ } test_volumes[] = {
+ { false, 50, 100 },
+ { false, 99, 100 },
+ { true, 100, 100 },
+ { true, 101, 100 }
+ };
+
+ const struct {
+ bool expected;
+ bool approved;
+ } test_approved[] = {
+ { true, false },
+ { false, true }
+ };
+
+ const struct {
+ bool expected;
+ bool warnings_enabled;
+ } test_warnings_enabled[] = {
+ { true, true },
+ { false, false }
+ };
+
+ const struct {
+ bool expected;
+ bool multimedia_active;
+ } test_multimedia_active[] = {
+ { true, true },
+ { false, false }
+ };
+
+ for (const auto& outputs : test_outputs) {
+ for (const auto& volumes : test_volumes) {
+ for (const auto& approved : test_approved) {
+ for (const auto& warnings_enabled : test_warnings_enabled) {
+ for (const auto& multimedia_active : test_multimedia_active) {
+
+ notifications->clearNotifications();
+
+ // instantiate the test subjects
+ auto options = optionsMock();
+ auto volumeControl = volumeControlMock(options);
+ auto volumeWarning = volumeWarningMock(options);
+ auto accountsService = std::make_shared<AccountsServiceAccess>();
+ auto soundService = standardService(volumeControl, playerListMock(), options, volumeWarning, accountsService);
+
+ // run the test
+ options_mock_mock_set_loud_volume(OPTIONS_MOCK(options.get()), volumes.loud_volume);
+ options_mock_mock_set_loud_warning_enabled(OPTIONS_MOCK(options.get()), warnings_enabled.warnings_enabled);
+ volume_warning_mock_set_approved(VOLUME_WARNING_MOCK(volumeWarning.get()), approved.approved);
+ volume_warning_mock_set_multimedia_volume(VOLUME_WARNING_MOCK(volumeWarning.get()), volumes.volume);
+ volume_warning_mock_set_multimedia_active(VOLUME_WARNING_MOCK(volumeWarning.get()), multimedia_active.multimedia_active);
+ volume_control_mock_mock_set_active_output(VOLUME_CONTROL_MOCK(volumeControl.get()), outputs.output);
+
+ loop_until_notifications();
+
+ // check the result
+ auto notev = notifications->getNotifications();
+ const bool warning_expected = outputs.expected && volumes.expected && approved.expected && warnings_enabled.expected && multimedia_active.expected;
+ if (warning_expected) {
+ EXPECT_TRUE(volume_warning_get_active(volumeWarning.get()));
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-snap-decisions"]);
+ EXPECT_GVARIANT_EQ(nullptr, notev[0].hints["x-canonical-private-synchronous"]);
+ }
+ else {
+ EXPECT_FALSE(volume_warning_get_active(volumeWarning.get()));
+ ASSERT_EQ(1, notev.size());
+ EXPECT_GVARIANT_EQ(nullptr, notev[0].hints["x-canonical-snap-decisions"]);
+ EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-private-synchronous"]);
+ }
+
+ } // multimedia_active
+ } // warnings_enabled
+ } // approved
+ } // volumes
+ } // outputs
}
diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp
index 37de377..b0e4862 100644
--- a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp
+++ b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp
@@ -46,3 +46,18 @@ void AccountsServiceSoundMock::setVolume(double volume)
"Volume",
property("Volume"));
}
+
+QString AccountsServiceSoundMock::lastRunningPlayer() const
+{
+ return lastRunningPlayer_;
+}
+
+void AccountsServiceSoundMock::setLastRunningPlayer(QString const & lastRunningPlayer)
+{
+ lastRunningPlayer_ = lastRunningPlayer;
+ notifier_.notifyPropertyChanged(QDBusConnection::systemBus(),
+ ACCOUNTS_SOUND_INTERFACE,
+ USER_PATH,
+ "LastRunningPlayer",
+ property("LastRunningPlayer"));
+}
diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h
index bb3dbe8..fd5104e 100644
--- a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h
+++ b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h
@@ -37,10 +37,13 @@ class AccountsServiceSoundMock : public QObject, protected QDBusContext
{
Q_OBJECT
Q_PROPERTY(double Volume READ volume WRITE setVolume)
+ Q_PROPERTY(QString LastRunningPlayer READ lastRunningPlayer WRITE setLastRunningPlayer)
public Q_SLOTS:
double volume() const;
void setVolume(double volume);
+ QString lastRunningPlayer() const;
+ void setLastRunningPlayer(QString const & lastRunningPlayer);
public:
AccountsServiceSoundMock(QObject* parent = 0);
@@ -48,6 +51,7 @@ public:
private:
double volume_;
+ QString lastRunningPlayer_;
DBusPropertiesNotifier notifier_;
};
diff --git a/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml b/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml
index 859cd46..daf82e5 100644
--- a/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml
+++ b/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml
@@ -2,5 +2,6 @@
<node>
<interface name="com.ubuntu.AccountsService.Sound">
<property name="Volume" type="d" access="readwrite"/>
+ <property name="LastRunningPlayer" type="s" access="readwrite"/>
</interface>
</node> \ No newline at end of file
diff --git a/tests/sound-menu.cc b/tests/sound-menu.cc
index 2576d19..79ae703 100644
--- a/tests/sound-menu.cc
+++ b/tests/sound-menu.cc
@@ -62,7 +62,7 @@ class SoundMenuTest : public ::testing::Test
void check_player_control_buttons(bool canPlay, bool canNext, bool canPrev)
{
- SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE, "");
+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
MediaPlayerTrack * track = media_player_track_new("Artist", "Title", "Album", "http://art.url");
@@ -96,22 +96,22 @@ class SoundMenuTest : public ::testing::Test
/* Player control */
verify_item_attribute(section, 1, "x-canonical-type", g_variant_new_string("com.canonical.unity.playback-item"));
- //verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string(""));
- if (!canPlay) {
+ //verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string(""));
+ if (!canPlay) {
verify_item_attribute_is_not_set(section, 1, "x-canonical-play-action", G_VARIANT_TYPE_STRING);
- } else {
- verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string("indicator.play.player-id"));
- }
- if (!canNext) {
+ } else {
+ verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string("indicator.play.player-id"));
+ }
+ if (!canNext) {
verify_item_attribute_is_not_set(section, 1, "x-canonical-next-action", G_VARIANT_TYPE_STRING);
- } else {
- verify_item_attribute(section, 1, "x-canonical-next-action", g_variant_new_string("indicator.next.player-id"));
- }
- if (!canPrev) {
+ } else {
+ verify_item_attribute(section, 1, "x-canonical-next-action", g_variant_new_string("indicator.next.player-id"));
+ }
+ if (!canPrev) {
verify_item_attribute_is_not_set(section, 1, "x-canonical-previous-action", G_VARIANT_TYPE_STRING);
- } else {
- verify_item_attribute(section, 1, "x-canonical-previous-action", g_variant_new_string("indicator.previous.player-id"));
- }
+ } else {
+ verify_item_attribute(section, 1, "x-canonical-previous-action", g_variant_new_string("indicator.previous.player-id"));
+ }
g_clear_object(&section);
@@ -125,7 +125,7 @@ class SoundMenuTest : public ::testing::Test
};
TEST_F(SoundMenuTest, BasicObject) {
- SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE, "");
+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
ASSERT_NE(nullptr, menu);
@@ -134,7 +134,7 @@ TEST_F(SoundMenuTest, BasicObject) {
}
TEST_F(SoundMenuTest, AddRemovePlayer) {
- SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE, "");
+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
MediaPlayerTrack * track = media_player_track_new("Artist", "Title", "Album", "http://art.url");
diff --git a/tests/volume-control-test.cc b/tests/volume-control-test.cc
index 11fa4ff..62319a0 100644
--- a/tests/volume-control-test.cc
+++ b/tests/volume-control-test.cc
@@ -29,64 +29,65 @@ extern "C" {
class VolumeControlTest : public ::testing::Test
{
- protected:
- DbusTestService * service = NULL;
- GDBusConnection * session = NULL;
-
- virtual void SetUp() override {
-
- g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
- g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
-
- service = dbus_test_service_new(NULL);
- dbus_test_service_start_tasks(service);
-
- session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
- ASSERT_NE(nullptr, session);
- g_dbus_connection_set_exit_on_close(session, FALSE);
- g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session);
- }
-
- virtual void TearDown() {
- g_clear_object(&service);
-
- g_object_unref(session);
-
- unsigned int cleartry = 0;
- while (session != NULL && cleartry < 100) {
- loop(100);
- cleartry++;
- }
-
- ASSERT_EQ(nullptr, session);
- }
-
- static gboolean timeout_cb (gpointer user_data) {
- GMainLoop * loop = static_cast<GMainLoop *>(user_data);
- g_main_loop_quit(loop);
- return G_SOURCE_REMOVE;
- }
-
- void loop (unsigned int ms) {
- GMainLoop * loop = g_main_loop_new(NULL, FALSE);
- g_timeout_add(ms, timeout_cb, loop);
- g_main_loop_run(loop);
- g_main_loop_unref(loop);
- }
+ protected:
+ DbusTestService * service = NULL;
+ GDBusConnection * session = NULL;
+
+ virtual void SetUp() override {
+
+ g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
+ g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
+
+ service = dbus_test_service_new(NULL);
+ dbus_test_service_start_tasks(service);
+
+ session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
+ ASSERT_NE(nullptr, session);
+ g_dbus_connection_set_exit_on_close(session, FALSE);
+ g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session);
+ }
+
+ virtual void TearDown() {
+ g_clear_object(&service);
+
+ g_object_unref(session);
+
+ unsigned int cleartry = 0;
+ while (session != NULL && cleartry < 100) {
+ loop(100);
+ cleartry++;
+ }
+
+ ASSERT_EQ(nullptr, session);
+ }
+
+ static gboolean timeout_cb (gpointer user_data) {
+ GMainLoop * loop = static_cast<GMainLoop *>(user_data);
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ }
+
+ void loop (unsigned int ms) {
+ GMainLoop * loop = g_main_loop_new(NULL, FALSE);
+ g_timeout_add(ms, timeout_cb, loop);
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+ }
};
TEST_F(VolumeControlTest, BasicObject) {
- auto options = options_mock_new();
- auto pgloop = pa_glib_mainloop_new(NULL);
- auto control = volume_control_pulse_new(INDICATOR_SOUND_OPTIONS(options), pgloop);
+ auto options = options_mock_new();
+ auto pgloop = pa_glib_mainloop_new(NULL);
+ auto accounts_service_access = accounts_service_access_new();
+ auto control = volume_control_pulse_new(INDICATOR_SOUND_OPTIONS(options), pgloop, accounts_service_access);
- /* Setup the PA backend */
- loop(100);
+ /* Setup the PA backend */
+ loop(100);
- /* Ready */
- EXPECT_TRUE(volume_control_get_ready(VOLUME_CONTROL(control)));
+ /* Ready */
+ EXPECT_TRUE(volume_control_get_ready(VOLUME_CONTROL(control)));
- g_clear_object(&control);
- g_clear_object(&options);
- g_clear_pointer(&pgloop, pa_glib_mainloop_free);
+ g_clear_object(&control);
+ g_clear_object(&options);
+ g_clear_pointer(&pgloop, pa_glib_mainloop_free);
}