aboutsummaryrefslogtreecommitdiff
path: root/tests/test-service.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-service.cc')
-rw-r--r--tests/test-service.cc868
1 files changed, 772 insertions, 96 deletions
diff --git a/tests/test-service.cc b/tests/test-service.cc
index b1ca5bc..b199175 100644
--- a/tests/test-service.cc
+++ b/tests/test-service.cc
@@ -17,70 +17,353 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <glib-object.h>
-#include <gio/gio.h>
-#include <glib.h>
-
-#include <gtest/gtest.h>
-
-#include "shared-names.h"
+#include "gtest-dbus-fixture.h"
+#include "service.h"
+#include "backend-mock.h"
+#include "backend-mock-users.h"
+#include "backend-mock-guest.h"
+#include "backend-mock-actions.h"
/***
****
***/
-#define INDICATOR_SERVICE_OBJECT_PATH "/org/ayatana/indicator/service"
-#define INDICATOR_SERVICE_INTERFACE_NAME "org.ayatana.indicator.service"
+#if 0
+namespace
+{
+ void
+ dump_menu_model (GMenuModel * model, int depth)
+ {
+ GString * indent = g_string_new_len (" ", (depth*4));
+ const int n = g_menu_model_get_n_items (model);
+ g_message ("%s depth[%d] menu model[%p] has %d items", indent->str, depth, (void*)model, n);
+
+ for (int i=0; i<n; ++i)
+ {
+ const char * name;
+ GMenuModel * link_value;
+ GVariant * attribute_value;
+
+ GMenuAttributeIter * attribute_iter = g_menu_model_iterate_item_attributes (model, i);
+ while (g_menu_attribute_iter_get_next (attribute_iter, &name, &attribute_value))
+ {
+ char * str = g_variant_print (attribute_value, TRUE);
+ g_message ("%s depth[%d] menu model[%p] item[%d] attribute key[%s] value[%s]", indent->str, depth, (void*)model, i, name, str);
+ g_free (str);
+ g_variant_unref (attribute_value);
+ }
+ g_clear_object (&attribute_iter);
+
+ GMenuLinkIter * link_iter = g_menu_model_iterate_item_links (model, i);
+ while (g_menu_link_iter_get_next (link_iter, &name, &link_value))
+ {
+ g_message ("%s depth[%d] menu model[%p] item[%d] attribute key[%s] model[%p]", indent->str, depth, (void*)model, i, name, (void*)link_value);
+ dump_menu_model (link_value, depth+1);
+ g_object_unref (link_value);
+ }
+ g_clear_object (&link_iter);
+ }
+ g_string_free (indent, TRUE);
+ }
+}
+#endif
-class ClientTest : public ::testing::Test
+
+/* cppcheck-suppress noConstructor */
+class ServiceTest: public GTestDBusFixture
{
+ typedef GTestDBusFixture super;
+
+ enum { TIME_LIMIT_SEC = 10 };
+
+ private:
+
+ static void on_name_appeared (GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * name G_GNUC_UNUSED,
+ const gchar * name_owner G_GNUC_UNUSED,
+ gpointer gself)
+ {
+ g_main_loop_quit (static_cast<ServiceTest*>(gself)->loop);
+ }
+
+ GSList * menu_references;
+
+ gboolean any_item_changed;
+
+ static void on_items_changed (GMenuModel * model G_GNUC_UNUSED,
+ gint position G_GNUC_UNUSED,
+ gint removed G_GNUC_UNUSED,
+ gint added G_GNUC_UNUSED,
+ gpointer any_item_changed)
+ {
+ *((gboolean*)any_item_changed) = true;
+ }
+
+ protected:
+
+ void activate_subtree (GMenuModel * model)
+ {
+ // query the GDBusMenuModel for information to activate it
+ int n = g_menu_model_get_n_items (model);
+ if (!n)
+ {
+ // give the model a moment to populate its info
+ wait_msec (100);
+ n = g_menu_model_get_n_items (model);
+ }
+
+ // keep a ref so that it stays activated
+ menu_references = g_slist_prepend (menu_references, g_object_ref(model));
+
+ g_signal_connect (model, "items-changed", G_CALLBACK(on_items_changed), &any_item_changed);
+
+ // recurse
+ for (int i=0; i<n; ++i)
+ {
+ GMenuModel * link;
+ GMenuLinkIter * iter = g_menu_model_iterate_item_links (model, i);
+ while (g_menu_link_iter_get_next (iter, NULL, &link))
+ {
+ activate_subtree (link);
+ g_object_unref (link);
+ }
+ g_clear_object (&iter);
+ }
+ }
+
+ void sync_menu (void)
+ {
+ g_slist_free_full (menu_references, (GDestroyNotify)g_object_unref);
+ menu_references = NULL;
+ activate_subtree (G_MENU_MODEL (menu_model));
+ }
+
+ GDBusMenuModel * menu_model;
+ GDBusActionGroup * action_group;
+ IndicatorSessionService * service;
+ GTimer * timer;
+ GSettings * indicator_settings;
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ menu_references = NULL;
+ any_item_changed = FALSE;
+
+ timer = g_timer_new ();
+ mock_settings = g_settings_new ("com.canonical.indicator.session.backendmock");
+ mock_actions = indicator_session_actions_mock_new ();
+ mock_users = indicator_session_users_mock_new ();
+ mock_guest = indicator_session_guest_mock_new ();
+ indicator_settings = g_settings_new ("com.canonical.indicator.session");
+
+ // Start an IndicatorSessionService and wait for it to appear on the bus.
+ // This way our calls to g_dbus_*_get() in the next paragraph won't activate
+ // a second copy of the service...
+ service = indicator_session_service_new ();
+
+ // wait for the service to show up on the bus
+ const guint watch_id = g_bus_watch_name_on_connection (conn,
+ "com.canonical.indicator.session",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_name_appeared, // quits the loop
+ NULL, this, NULL);
+ const guint timer_id = g_timeout_add_seconds (TIME_LIMIT_SEC, (GSourceFunc)g_main_loop_quit, loop);
+ g_main_loop_run (loop);
+ g_source_remove (timer_id);
+ g_bus_unwatch_name (watch_id);
+ ASSERT_FALSE (times_up());
+
+ // get the actions & menus that the service exported.
+ action_group = g_dbus_action_group_get (conn,
+ "com.canonical.indicator.session",
+ "/com/canonical/indicator/session");
+ menu_model = g_dbus_menu_model_get (conn,
+ "com.canonical.indicator.session",
+ "/com/canonical/indicator/session/desktop");
+ // the actions are added asynchronously, so wait for the actions
+ if (!g_action_group_has_action (G_ACTION_GROUP(action_group), "about"))
+ wait_for_signal (action_group, "action-added");
+ // activate all the groups in the menu so it'll be ready when we need it
+ g_signal_connect (menu_model, "items-changed", G_CALLBACK(on_items_changed), &any_item_changed);
+ sync_menu ();
+ }
+
+ virtual void TearDown ()
+ {
+ g_clear_pointer (&timer, g_timer_destroy);
+
+ g_slist_free_full (menu_references, (GDestroyNotify)g_object_unref);
+ menu_references = NULL;
+ g_clear_object (&menu_model);
+
+ g_clear_object (&action_group);
+ g_clear_object (&mock_settings);
+ g_clear_object (&indicator_settings);
+ g_clear_object (&service);
+ g_clear_object (&mock_actions);
+ g_clear_object (&mock_users);
+ g_clear_object (&mock_guest);
+ wait_msec (100);
+
+ super :: TearDown ();
+ }
+
+ protected:
+
+ bool times_up () const
+ {
+ return g_timer_elapsed (timer, NULL) >= TIME_LIMIT_SEC;
+ }
+
+ void wait_for_has_action (const char * name)
+ {
+ while (!g_action_group_has_action (G_ACTION_GROUP(action_group), name) && !times_up())
+ wait_msec (50);
+
+ ASSERT_FALSE (times_up());
+ ASSERT_TRUE (g_action_group_has_action (G_ACTION_GROUP(action_group), name));
+ }
+
+ void wait_for_menu_resync (void)
+ {
+ any_item_changed = false;
+ while (!times_up() && !any_item_changed)
+ wait_msec (50);
+ g_warn_if_fail (any_item_changed);
+ sync_menu ();
+ }
+
+ protected:
+
+ void check_last_command_is (const char * expected)
+ {
+ char * str = g_settings_get_string (mock_settings, "last-command");
+ ASSERT_STREQ (expected, str);
+ g_free (str);
+ }
+
+ void test_simple_action (const char * action_name)
+ {
+ wait_for_has_action (action_name);
+
+ g_action_group_activate_action (G_ACTION_GROUP (action_group), action_name, NULL);
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is (action_name);
+ }
+
protected:
- GTestDBus * test_dbus;
- GDBusConnection * session_bus;
- GMainLoop * main_loop;
+ bool find_menu_item_for_action (const char * action_key, GMenuModel ** setme, int * item_index)
+ {
+ bool success = false;
+
+ for (GSList * l=menu_references; !success && (l!=NULL); l=l->next)
+ {
+ GMenuModel * mm = G_MENU_MODEL (l->data);
+ const int n = g_menu_model_get_n_items (mm);
+
+ for (int i=0; !success && i<n; ++i)
+ {
+ char * action = NULL;
+ if (!g_menu_model_get_item_attribute (mm, i, G_MENU_ATTRIBUTE_ACTION, "s", &action))
+ continue;
+
+ if ((success = !g_strcmp0 (action, action_key)))
+ {
+ if (setme != NULL)
+ *setme = G_MENU_MODEL (g_object_ref (G_OBJECT(mm)));
+
+ if (item_index != NULL)
+ *item_index = i;
+ }
+
+ g_free (action);
+ }
+ }
+
+ return success;
+ }
+
+ bool action_menuitem_exists (const char * action_name)
+ {
+ bool found;
+ GMenuModel * model = 0;
+ int pos = -1;
+
+ if ((found = find_menu_item_for_action (action_name, &model, &pos)))
+ g_object_unref (G_OBJECT(model));
+
+ return found;
+ }
+
+ bool action_menuitem_label_is_ellipsized (const char * action_name)
+ {
+ int pos = -1;
+ GMenuModel * model = 0;
+ bool ellipsized = false;
+ char * label = NULL;
+
+ if (find_menu_item_for_action (action_name, &model, &pos))
+ {
+ g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_object_unref (G_OBJECT(model));
+ }
+
+ ellipsized = (label != NULL) && g_str_has_suffix (label, "\342\200\246");
+ g_free (label);
+ return ellipsized;
+ }
- virtual void SetUp()
+ void check_header (const char * expected_label, const char * expected_icon, const char * expected_a11y)
{
- test_dbus = NULL;
- session_bus = NULL;
- main_loop = NULL;
+ GVariant * state = g_action_group_get_action_state (G_ACTION_GROUP(action_group), "_header");
+ ASSERT_TRUE (state != NULL);
+ ASSERT_TRUE (g_variant_is_of_type (state, G_VARIANT_TYPE ("a{sv}")));
- static bool first_run = true;
- if (first_run)
+ if (expected_label != NULL)
{
- g_setenv ("INDICATOR_SERVICE_SHUTDOWN_TIMEOUT", "1000", TRUE);
- g_unsetenv ("INDICATOR_ALLOW_NO_WATCHERS");
- g_unsetenv ("INDICATOR_SERVICE_REPLACE_MODE");
- g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
- g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
- first_run = false;
+ GVariant * v = g_variant_lookup_value (state, "label", G_VARIANT_TYPE_STRING);
+ if (!v) // if no label in the state, expected_label must be an empty string
+ ASSERT_FALSE (*expected_label);
+ else
+ ASSERT_STREQ (expected_label, g_variant_get_string (v, NULL));
}
- main_loop = g_main_loop_new (NULL, FALSE);
- // pull up a test dbus that's pointed at our test .service file
- test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
- g_debug (G_STRLOC" service dir path is \"%s\"", INDICATOR_SERVICE_DIR);
- g_test_dbus_add_service_dir (test_dbus, INDICATOR_SERVICE_DIR);
+ if (expected_a11y != NULL)
+ {
+ GVariant * v = g_variant_lookup_value (state, "accessible-desc", G_VARIANT_TYPE_STRING);
+ ASSERT_TRUE (v != NULL);
+ ASSERT_STREQ (expected_a11y, g_variant_get_string (v, NULL));
+ g_variant_unref (v);
+ }
- // allow the service to exist w/o a sync indicator
- g_setenv ("INDICATOR_ALLOW_NO_WATCHERS", "1", TRUE);
+ if (expected_icon != NULL)
+ {
+ GVariant * v = g_variant_lookup_value (state, "icon", NULL);
+ GIcon * expected = g_themed_icon_new (expected_icon);
+ GIcon * actual = g_icon_deserialize (v);
+ ASSERT_TRUE (g_icon_equal (expected, actual));
+ g_object_unref (actual);
+ g_object_unref (expected);
+ g_variant_unref (v);
+ }
+
+ // the session menu is always visible...
+ gboolean visible = false;
+ g_variant_lookup (state, "visible", "b", &visible);
+ ASSERT_TRUE (visible);
- g_test_dbus_up (test_dbus);
- g_debug (G_STRLOC" this test bus' address is \"%s\"", g_test_dbus_get_bus_address(test_dbus));
- session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- g_debug (G_STRLOC" the dbus connection %p unique name is \"%s\"", session_bus, g_dbus_connection_get_unique_name(session_bus));
- g_debug (G_STRLOC" the dbus connection %p refcount is %d", session_bus, G_OBJECT(session_bus)->ref_count);
+ g_variant_unref (state);
}
- // undo SetUp
- virtual void TearDown()
+ void check_label (const char * expected_label, GMenuModel * model, int pos)
{
- g_clear_object (&session_bus);
- g_debug (G_STRLOC" tearing down the bus");
- g_test_dbus_down (test_dbus);
- g_clear_object (&test_dbus);
- g_clear_pointer (&main_loop, g_main_loop_unref);
+ char * label = NULL;
+ ASSERT_TRUE (g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label));
+ ASSERT_STREQ (expected_label, label);
+ g_free (label);
}
};
@@ -88,58 +371,451 @@ class ClientTest : public ::testing::Test
****
***/
+#if 0
+TEST_F (ServiceTest, HelloWorld)
+{
+ ASSERT_TRUE (true);
+}
+#endif
+
+TEST_F (ServiceTest, About)
+{
+ test_simple_action ("about");
+}
+
+TEST_F (ServiceTest, Help)
+{
+ test_simple_action ("help");
+}
+
+TEST_F (ServiceTest, Hibernate)
+{
+ test_simple_action ("hibernate");
+}
+
+TEST_F (ServiceTest, Settings)
+{
+ test_simple_action ("settings");
+}
+
+TEST_F (ServiceTest, Logout)
+{
+ test_simple_action ("logout");
+}
+
+TEST_F (ServiceTest, PowerOff)
+{
+ test_simple_action ("power-off");
+}
+
+TEST_F (ServiceTest, Reboot)
+{
+ test_simple_action ("reboot");
+}
+
+TEST_F (ServiceTest, SwitchToScreensaver)
+{
+ test_simple_action ("switch-to-screensaver");
+}
+
+TEST_F (ServiceTest, SwitchToGuest)
+{
+ test_simple_action ("switch-to-guest");
+}
+
+TEST_F (ServiceTest, SwitchToGreeter)
+{
+ test_simple_action ("switch-to-greeter");
+}
+
+TEST_F (ServiceTest, Suspend)
+{
+ test_simple_action ("suspend");
+}
+
+#if 0
+namespace
+{
+ gboolean
+ find_menu_item_for_action (GMenuModel * top, const char * action_key, GMenuModel ** setme, int * item_index)
+ {
+ gboolean success = FALSE;
+ const int n = g_menu_model_get_n_items (top);
+
+ for (int i=0; !success && i<n; ++i)
+ {
+ char * action = NULL;
+ if (g_menu_model_get_item_attribute (top, i, G_MENU_ATTRIBUTE_ACTION, "s", &action))
+ {
+ if ((success = !g_strcmp0 (action, action_key)))
+ {
+ *setme = G_MENU_MODEL (g_object_ref (G_OBJECT(top)));
+ *item_index = i;
+ }
+
+ g_free (action);
+ }
+
+ const char * name = NULL;
+ GMenuModel * link_value = NULL;
+ GMenuLinkIter * link_iter = g_menu_model_iterate_item_links (top, i);
+ while (!success && g_menu_link_iter_get_next (link_iter, &name, &link_value))
+ {
+ success = find_menu_item_for_action (link_value, action_key, setme, item_index);
+ g_object_unref (link_value);
+ }
+ g_clear_object (&link_iter);
+ }
+
+ return success;
+ }
+
+ gchar *
+ get_menu_label_for_action (GMenuModel * top, const char * action)
+ {
+ int pos;
+ GMenuModel * model;
+ gchar * label = NULL;
+
+ if (find_menu_item_for_action (top, action, &model, &pos))
+ {
+ g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_object_unref (G_OBJECT(model));
+ }
+
+ return label;
+ }
+
+ gboolean str_is_ellipsized (const char * str)
+ {
+ g_assert (str != NULL);
+ return g_str_has_suffix (str, "\342\200\246");
+ }
+}
+#endif
+
+TEST_F (ServiceTest, ConfirmationDisabledByBackend)
+{
+ const char * const confirm_supported_key = "can-prompt";
+ const char * const confirm_disabled_key = "suppress-logout-restart-shutdown";
+
+ bool confirm_supported = g_settings_get_boolean (mock_settings, confirm_supported_key);
+ bool confirm_disabled = g_settings_get_boolean (indicator_settings, confirm_disabled_key);
+ bool confirm = confirm_supported && !confirm_disabled;
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // now toggle the can-prompt flag
+ confirm_supported = !confirm_supported;
+ g_settings_set_boolean (mock_settings, confirm_supported_key, confirm_supported);
+ confirm = confirm_supported && !confirm_disabled;
+
+ wait_for_menu_resync ();
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // cleanup
+ g_settings_reset (mock_settings, confirm_supported_key);
+}
+
+TEST_F (ServiceTest, ConfirmationDisabledByUser)
+{
+ const char * const confirm_supported_key = "can-prompt";
+ const char * const confirm_disabled_key = "suppress-logout-restart-shutdown";
+
+ bool confirm_supported = g_settings_get_boolean (mock_settings, confirm_supported_key);
+ bool confirm_disabled = g_settings_get_boolean (indicator_settings, confirm_disabled_key);
+ bool confirm = confirm_supported && !confirm_disabled;
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // now toggle the can-prompt flag
+ confirm_disabled = !confirm_disabled;
+ g_settings_set_boolean (indicator_settings, confirm_disabled_key, confirm_disabled);
+ confirm = confirm_supported && !confirm_disabled;
+
+ wait_for_menu_resync ();
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // cleanup
+ g_settings_reset (indicator_settings, confirm_disabled_key);
+}
+
/**
- * This is a basic test to see if we can launch indicator-session-service
- * and call its "GetUserRealName" method. The test succeeds if we can launch
- * the service, call the method, and get response that equals g_get_real_name().
- *
- * (You may be wondering why GetUserRealName() exists at all, instead of clients
- * using g_get_real_name(). It's because the former updates itslef when the user
- * edits his real name, while the latter returns its cached copy of the old name.)
+ * Check that the default menu has items for each of these actions
*/
-TEST_F (ClientTest, TestCanStartService)
-{
- GError * error;
- GVariant * result;
- const gchar * name;
-
- // call GetUserRealName(), which as a side effect should activate
- // indicator-session-service via the .service file in the tests/ directory
- error = NULL;
- result = g_dbus_connection_call_sync (session_bus,
- INDICATOR_SESSION_DBUS_NAME,
- INDICATOR_SESSION_SERVICE_DBUS_OBJECT,
- INDICATOR_SESSION_SERVICE_DBUS_IFACE,
- "GetUserRealName",
- NULL,
- G_VARIANT_TYPE("(s)"),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- &error);
-
- EXPECT_TRUE (error == NULL);
- ASSERT_TRUE (result != NULL);
-
- if (error != NULL)
- {
- g_warning ("GetUserRealName failed: %s", error->message);
- g_clear_error (&error);
- }
-
- name = NULL;
- g_variant_get (result, "(&s)", &name);
- ASSERT_STREQ (g_get_real_name(), name);
- g_clear_pointer (&result, g_variant_unref);
-
- // call IndicatorService's Shutdown() method for a clean exit
- result = g_dbus_connection_call_sync (session_bus,
- INDICATOR_SESSION_DBUS_NAME,
- "/org/ayatana/indicator/service",
- "org.ayatana.indicator.service",
- "Shutdown", NULL,
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL);
- g_clear_pointer (&result, g_variant_unref);
+TEST_F (ServiceTest, DefaultMenuItems)
+{
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.about", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.help", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.settings", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-guest", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.logout", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.suspend", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.hibernate", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.power-off", NULL, NULL));
+}
+
+TEST_F (ServiceTest, OnlineAccountError)
+{
+ bool err;
+ int pos = -1;
+ GMenuModel * model = 0;
+ const char * const error_key = "has-online-account-error";
+
+ // check the initial default header state
+ check_header ("", "system-devices-panel", "System");
+
+ // check that the menuitems' existence matches the error flag
+ err = g_settings_get_boolean (mock_settings, error_key);
+ ASSERT_FALSE (err);
+ ASSERT_EQ (err, find_menu_item_for_action ("indicator.online-accounts", &model, &pos));
+ g_clear_object (&model);
+
+ // now toggle the error flag
+ err = !err;
+ g_settings_set_boolean (mock_settings, error_key, err);
+
+ // wait for the _header action and error menuitem to update
+ wait_for_menu_resync ();
+
+ // check that the menuitems' existence matches the error flag
+ ASSERT_TRUE (g_settings_get_boolean (mock_settings, error_key));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.online-accounts", &model, &pos));
+ g_clear_object (&model);
+
+ // check that the service has a corresponding action
+ ASSERT_TRUE (g_action_group_has_action (G_ACTION_GROUP(action_group), "online-accounts"));
+ ASSERT_TRUE (g_action_group_get_action_enabled (G_ACTION_GROUP(action_group), "online-accounts"));
+
+ // confirm that activating the action is handled by the service
+ g_action_group_activate_action (G_ACTION_GROUP(action_group), "online-accounts", NULL);
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is ("online-accounts");
+
+ // check that the header's icon and a11y adjusted to the error state
+ check_header ("", "system-devices-panel-alert", "System (Attention Required)");
+
+ // cleanup
+ g_settings_reset (mock_settings, error_key);
+}
+
+namespace
+{
+ gboolean set_live_session_to_true (gpointer unused G_GNUC_UNUSED)
+ {
+ const char * const live_session_key = "is-live-session";
+ g_settings_set_boolean (mock_settings, live_session_key, true);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (ServiceTest, LiveSession)
+{
+ gboolean b;
+ const char * const live_session_key = "is-live-session";
+
+ // default BackendMock is not a live session
+ ASSERT_FALSE (g_settings_get_boolean (mock_settings, live_session_key));
+ g_object_get (mock_users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_FALSE (b);
+
+ // confirm that we can see live sessions
+ g_idle_add (set_live_session_to_true, NULL);
+ wait_for_signal (mock_users, "notify::" INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION);
+ ASSERT_TRUE (g_settings_get_boolean (mock_settings, live_session_key));
+ wait_msec (50);
+ g_object_get (mock_users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_TRUE (b);
+
+ // cleanup
+ g_settings_reset (mock_settings, live_session_key);
+}
+
+
+TEST_F (ServiceTest, User)
+{
+ const char * const error_key = "has-online-account-error";
+ const char * const show_name_key = "show-real-name-on-panel";
+
+ struct {
+ guint uid;
+ guint64 login_frequency;
+ const gchar * user_name;
+ const gchar * real_name;
+ } account_info[] = {
+ { 101, 134, "whartnell", "First Doctor" },
+ { 102, 119, "ptroughton", "Second Doctor" },
+ { 103, 128, "jpertwee", "Third Doctor" },
+ { 104, 172, "tbaker", "Fourth Doctor" },
+ { 105, 69, "pdavison", "Fifth Doctor" },
+ { 106, 31, "cbaker", "Sixth Doctor" },
+ { 107, 42, "smccoy", "Seventh Doctor" },
+ { 108, 1, "pmcgann", "Eigth Doctor" },
+ { 109, 13, "ceccleston", "Ninth Doctor" },
+ { 110, 47, "dtennant", "Tenth Doctor" },
+ { 111, 34, "msmith", "Eleventh Doctor" },
+ { 201, 1, "rhurndall", "First Doctor" }
+ };
+
+ // Find the switcher menu model.
+ // In BackendMock's default setup, it will only two menuitems: greeter & guest
+ int pos = 0;
+ GMenuModel * switch_menu = 0;
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (2, g_menu_model_get_n_items (switch_menu));
+ g_clear_object (&switch_menu);
+
+ // now add some users
+ IndicatorSessionUser * users[12];
+ for (int i=0; i<12; ++i)
+ users[i] = 0;
+ for (int i=0; i<5; ++i)
+ {
+ IndicatorSessionUser * u = g_new0 (IndicatorSessionUser, 1);
+ u->is_current_user = false;
+ u->is_logged_in = false;
+ u->uid = account_info[i].uid;
+ u->login_frequency = account_info[i].login_frequency;
+ u->user_name = g_strdup (account_info[i].user_name);
+ u->real_name = g_strdup (account_info[i].real_name);
+ indicator_session_users_mock_add_user (INDICATOR_SESSION_USERS_MOCK(mock_users), u);
+ users[i] = u;
+ }
+
+ wait_for_menu_resync ();
+
+ // now there should be 7 menuitems: greeter + guest + the five doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (7, g_menu_model_get_n_items (switch_menu));
+ // confirm that the doctor names are sorted
+ check_label ("Fifth Doctor", switch_menu, 2);
+ check_label ("First Doctor", switch_menu, 3);
+ check_label ("Fourth Doctor", switch_menu, 4);
+ check_label ("Second Doctor", switch_menu, 5);
+ check_label ("Third Doctor", switch_menu, 6);
+ g_clear_object (&switch_menu);
+
+ // now remove a couple of 'em
+ indicator_session_users_mock_remove_user (INDICATOR_SESSION_USERS_MOCK(mock_users), account_info[3].uid);
+ indicator_session_users_mock_remove_user (INDICATOR_SESSION_USERS_MOCK(mock_users), account_info[4].uid);
+
+ wait_for_menu_resync ();
+
+ // now there should be 5 menuitems: greeter + guest + the three doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (5, g_menu_model_get_n_items (switch_menu));
+ // confirm that the doctor names are sorted
+ check_label ("First Doctor", switch_menu, 2);
+ check_label ("Second Doctor", switch_menu, 3);
+ check_label ("Third Doctor", switch_menu, 4);
+ g_clear_object (&switch_menu);
+
+ // now let's have the third one be the current user
+ users[2]->is_current_user = true;
+ users[2]->is_logged_in = true;
+ indicator_session_users_changed (mock_users, users[2]->uid);
+
+ wait_for_menu_resync ();
+
+ // now there should be 5 menuitems: greeter + guest + the three doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (5, g_menu_model_get_n_items (switch_menu));
+ g_clear_object (&switch_menu);
+
+ // oh hey, while we've got an active user let's check the header
+ ASSERT_FALSE (g_settings_get_boolean (indicator_settings, show_name_key));
+ ASSERT_FALSE (g_settings_get_boolean (mock_settings, error_key));
+ check_header ("", "system-devices-panel", "System");
+ g_settings_set_boolean (indicator_settings, show_name_key, true);
+ wait_for_signal (action_group, "action-state-changed");
+ check_header ("Third Doctor", "system-devices-panel", "System, Third Doctor");
+ g_settings_set_boolean (mock_settings, error_key, true);
+ wait_for_signal (action_group, "action-state-changed");
+ check_header ("Third Doctor", "system-devices-panel-alert", "System, Third Doctor (Attention Required)");
+ g_settings_reset (mock_settings, error_key);
+ g_settings_reset (indicator_settings, show_name_key);
+ wait_for_menu_resync ();
+
+ // try setting the max user count to 2...
+ // since troughton has the fewest logins, he should get culled
+ g_object_set (service, "max-users", 2, NULL);
+ guint max_users;
+ g_object_get (service, "max-users", &max_users, NULL);
+ ASSERT_EQ (2, max_users);
+ wait_for_menu_resync ();
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (4, g_menu_model_get_n_items (switch_menu));
+ check_label ("First Doctor", switch_menu, 2);
+ check_label ("Third Doctor", switch_menu, 3);
+ g_clear_object (&switch_menu);
+
+ // add some more, test sorting and culling.
+ // add in all the doctors, but only show 7, and make msmith the current session
+ g_object_set (service, "max-users", 7, NULL);
+ g_object_get (service, "max-users", &max_users, NULL);
+ ASSERT_EQ (7, max_users);
+ for (int i=3; i<12; ++i)
+ {
+ IndicatorSessionUser * u = g_new0 (IndicatorSessionUser, 1);
+ u->is_current_user = false;
+ u->is_logged_in = false;
+ u->uid = 101 + i;
+ u->login_frequency = account_info[i].login_frequency;
+ u->user_name = g_strdup (account_info[i].user_name);
+ u->real_name = g_strdup (account_info[i].real_name);
+ indicator_session_users_mock_add_user (INDICATOR_SESSION_USERS_MOCK(mock_users), u);
+ users[i] = u;
+ }
+ users[2]->is_current_user = false;
+ indicator_session_users_changed (mock_users, users[2]->uid);
+ users[10]->is_current_user = true;
+ users[10]->is_logged_in = true;
+ indicator_session_users_changed (mock_users, users[10]->uid);
+ wait_for_menu_resync ();
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (9, g_menu_model_get_n_items (switch_menu));
+ check_label ("Eleventh Doctor", switch_menu, 2);
+ check_label ("Fifth Doctor", switch_menu, 3);
+ check_label ("First Doctor", switch_menu, 4);
+ check_label ("Fourth Doctor", switch_menu, 5);
+ check_label ("Second Doctor", switch_menu, 6);
+ check_label ("Tenth Doctor", switch_menu, 7);
+ check_label ("Third Doctor", switch_menu, 8);
+ g_clear_object (&switch_menu);
+
+ // now switch to one of the doctors
+ g_action_group_activate_action (G_ACTION_GROUP(action_group),
+ "switch-to-user",
+ g_variant_new_string("tbaker"));
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is ("switch-to-user::tbaker");
}