From 12713f3e388593422ce007fdf40840109c17b431 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 7 Jun 2010 11:50:42 +0200 Subject: [PATCH] Add tp_connection_get_allowed_presence_statuses and tp_connection_set_self_presence_async https://bugs.freedesktop.org/show_bug.cgi?id=24107 --- docs/reference/telepathy-glib-sections.txt | 12 + telepathy-glib/connection-internal.h | 8 + telepathy-glib/connection.c | 391 ++++++++++++++++++++++++++++ telepathy-glib/connection.h | 34 +++ tests/dbus/self-presence.c | 123 +++++++++ tests/lib/contacts-conn.c | 3 +- 6 files changed, 570 insertions(+), 1 deletions(-) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 9c1d3a5..58e3972 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -2824,16 +2824,28 @@ TP_IS_CONNECTION TP_IS_CONNECTION_CLASS TpConnectionPrivate tp_avatar_requirements_get_type +tp_status_spec_get_type tp_connection_get_feature_quark_core tp_connection_get_feature_quark_connected tp_connection_get_feature_quark_capabilities tp_connection_get_feature_quark_avatar_requirements +tp_connection_get_feature_quark_self_presence TP_TYPE_AVATAR_REQUIREMENTS TpAvatarRequirements tp_avatar_requirements_new tp_avatar_requirements_copy tp_avatar_requirements_destroy + +TP_CONNECTION_FEATURE_SELF_PRESENCE +tp_connection_get_allowed_presence_statuses +tp_connection_set_self_presence_async +tp_connection_set_self_presence_finish +TP_TYPE_STATUS_SPEC +TpStatusSpec +tp_status_spec_new +tp_status_spec_copy +tp_status_spec_destroy tp_cli_connection_callback_for_connect tp_cli_connection_call_connect diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h index 67a70ad..51d36ce 100644 --- a/telepathy-glib/connection-internal.h +++ b/telepathy-glib/connection-internal.h @@ -53,9 +53,13 @@ struct _TpConnectionPrivate { GArray *avatar_request_queue; guint avatar_request_idle_id; + GHashTable *allowed_presence_statuses; + gboolean final_statuses; + TpProxyPendingCall *introspection_call; unsigned fetching_rcc:1; unsigned fetching_avatar_requirements:1; + unsigned fetching_allowed_presence_statuses:1; unsigned ready:1; unsigned tracking_aliases_changed:1; @@ -77,6 +81,10 @@ void _tp_connection_remove_contact (TpConnection *self, TpHandle handle, TpContact *contact); TpContact *_tp_connection_lookup_contact (TpConnection *self, TpHandle handle); +TpStatusSpec *_tp_status_spec_new (TpConnectionPresenceType type, + const gchar *status, gboolean may_set_on_self, gboolean can_have_message) + G_GNUC_WARN_UNUSED_RESULT; + /* Actually implemented in contact.c, but having a contact-internal header * just for this would be overkill */ void _tp_contact_connection_invalidated (TpContact *contact); diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c index 67c0f32..f7bf153 100644 --- a/telepathy-glib/connection.c +++ b/telepathy-glib/connection.c @@ -193,6 +193,28 @@ tp_connection_get_feature_quark_avatar_requirements (void) } /** + * TP_CONNECTION_FEATURE_SELF_PRESENCE: + * + * Expands to a call to a function that returns a #GQuark representing the + * "self-presence" feature. + * + * When this feature is prepared, the allowed presence statuses for this + * Connection have been retrieved. Use + * tp_connection_get_allowed_presence_statuses() to get them once prepared. + * + * One can ask for a feature to be prepared using the + * tp_proxy_prepare_async() function, and waiting for it to callback. + * + * Since: 0.11.UNRELEASED + */ + +GQuark +tp_connection_get_feature_quark_self_presence (void) +{ + return g_quark_from_static_string ("tp-connection-feature-self-presence"); +} + +/** * TP_ERRORS_DISCONNECTED: * * #GError domain representing a Telepathy connection becoming disconnected. @@ -258,6 +280,7 @@ enum PROP_CONNECTION_READY, PROP_SELF_HANDLE, PROP_CAPABILITIES, + PROP_ALLOWED_PRESENCE_STATUSES, N_PROPS }; @@ -290,6 +313,11 @@ tp_connection_get_property (GObject *object, case PROP_CAPABILITIES: g_value_set_object (value, self->priv->capabilities); break; + case PROP_ALLOWED_PRESENCE_STATUSES: + if (self->priv->allowed_presence_statuses != NULL) + g_hash_table_ref (self->priv->allowed_presence_statuses); + g_value_set_boxed (value, self->priv->allowed_presence_statuses); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -446,6 +474,97 @@ tp_connection_maybe_prepare_avatar_requirements (TpProxy *proxy) } static void +tp_connection_get_allowed_presence_statuses_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpConnection *self = (TpConnection *) proxy; + GValue *value; + GHashTable *statuses; + GHashTableIter iter; + gpointer k, v; + + self->priv->fetching_allowed_presence_statuses = FALSE; + + if (error != NULL) + { + DEBUG ("Failed to get allowed presence statuses: %s", error->message); + goto finally; + } + + DEBUG ("SELF PRESENCE ready"); + + if (self->priv->allowed_presence_statuses != NULL) + g_hash_table_unref (self->priv->allowed_presence_statuses); + + self->priv->allowed_presence_statuses = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, (GDestroyNotify) tp_status_spec_destroy); + + value = g_hash_table_lookup (properties, "Statuses"); + statuses = g_value_get_boxed (value); + + g_hash_table_iter_init (&iter, statuses); + + while (g_hash_table_iter_next (&iter, &k, &v)) + { + GValueArray *status_array = (GValueArray *) v; + TpConnectionPresenceType type; + gboolean may_set_on_self, can_have_message; + TpStatusSpec *status = NULL; + + tp_value_array_unpack (status_array, 3, &type, &may_set_on_self, + &can_have_message); + + status = _tp_status_spec_new (type, k, may_set_on_self, can_have_message); + + g_hash_table_insert (self->priv->allowed_presence_statuses, g_strdup (k), + status); + } + +finally: + _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_SELF_PRESENCE, + self->priv->allowed_presence_statuses != NULL); + self->priv->final_statuses = self->priv->allowed_presence_statuses != NULL && + self->priv->introspecting_after_connected; +} + +static void +tp_connection_maybe_prepare_self_presence (TpProxy *proxy) +{ + TpConnection *self = (TpConnection *) proxy; + + if (self->priv->allowed_presence_statuses != NULL && + self->priv->final_statuses) + return; /* already done */ + + if (self->priv->introspecting_after_connected && + !tp_proxy_has_interface_by_id (proxy, + TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) + { + _tp_proxy_set_feature_prepared (proxy, + TP_CONNECTION_FEATURE_SELF_PRESENCE, FALSE); + return; + } + + if (!_tp_proxy_is_preparing (proxy, TP_CONNECTION_FEATURE_SELF_PRESENCE) && + self->priv->final_statuses) + return; /* not interested right now */ + + if (self->priv->fetching_allowed_presence_statuses) + return; /* Another Get operation is running */ + + self->priv->fetching_allowed_presence_statuses = TRUE; + + DEBUG ("%p: Requesting allowed presence statuses", self); + + tp_cli_dbus_properties_call_get_all (self, -1, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + tp_connection_get_allowed_presence_statuses_cb, NULL, NULL, NULL); +} + +static void tp_connection_continue_introspection (TpConnection *self) { if (tp_proxy_get_invalidated (self) != NULL) @@ -683,6 +802,10 @@ tp_connection_got_interfaces_cb (TpConnection *self, self->priv->introspect_needed = g_list_append ( self->priv->introspect_needed, introspect_contacts); } + else if (q == TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE) + { + tp_connection_maybe_prepare_self_presence (TP_PROXY (self)); + } } else { @@ -1004,6 +1127,7 @@ tp_connection_init (TpConnection *self) self->priv->status_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; self->priv->contacts = g_hash_table_new (g_direct_hash, g_direct_equal); self->priv->introspection_call = NULL; + self->priv->final_statuses = FALSE; } static void @@ -1078,6 +1202,12 @@ tp_connection_dispose (GObject *object) tp_clear_pointer (&self->priv->avatar_requirements, tp_avatar_requirements_destroy); + if (self->priv->allowed_presence_statuses != NULL) + { + g_hash_table_unref (self->priv->allowed_presence_statuses); + self->priv->allowed_presence_statuses = NULL; + } + ((GObjectClass *) tp_connection_parent_class)->dispose (object); } @@ -1086,6 +1216,7 @@ enum { FEAT_CONNECTED, FEAT_CAPABILITIES, FEAT_AVATAR_REQUIREMENTS, + FEAT_SELF_PRESENCE, N_FEAT }; @@ -1110,6 +1241,10 @@ tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_AVATAR_REQUIREMENTS].start_preparing = tp_connection_maybe_prepare_avatar_requirements; + features[FEAT_SELF_PRESENCE].name = TP_CONNECTION_FEATURE_SELF_PRESENCE; + features[FEAT_SELF_PRESENCE].start_preparing = + tp_connection_maybe_prepare_self_presence; + /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -1230,6 +1365,26 @@ tp_connection_class_init (TpConnectionClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CAPABILITIES, param_spec); + + /** + * TpConnection:allowed-presence-statuses: + * + * A dictionary of presence statuses valid for use in this connection, or NULL + * if we don't know yet. + * + * To wait for valid allowed presence statuses, call tp_proxy_prepare_async() + * with the feature %TP_CONNECTION_FEATURE_SELF_PRESENCE. + * + * Type: GLib.HashTable + * Transfer: container + */ + param_spec = g_param_spec_boxed ("allowed-presence-statuses", + "Allowed presence statuses", + "A dictionary of presence statuses valid for use in this connection", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_ALLOWED_PRESENCE_STATUSES, + param_spec); } /** @@ -2271,3 +2426,239 @@ tp_connection_get_detailed_error (TpConnection *self, } } } + +/** + * TpStatusSpec: + * @type: The type of a presence. This should not be used as a way to set + * statuses that the client does not recognise (see + * + * SetPresence in the Telepathy D-Bus Interface Specification), but + * may be used to check that the client's assumptions about a particular status + * name match the connection manager's. + * @status: The string identifier of this status. + * @may_set_on_self: If true, the user can set this status on themselves using + * tp_connection_set_self_presence_async(). + * @can_have_message: If true, a non-empty message can be set for this status. + * Otherwise, the empty string is the only acceptable message. (For instance, + * on IRC you can be Away with a status message, but if you are available you + * cannot set a status message.) + * + * The possible presence statuses for this connection in its current status. + * + * Since: 0.11.UNRELEASED + */ + +/** + * TP_TYPE_STATUS_SPEC: + * + * The boxed type of a #TpStatusSpec. + * + * Since: 0.11.UNRELEASED + */ +GType +tp_status_spec_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + type = g_boxed_type_register_static ( + g_intern_static_string ("TpStatusSpec"), + (GBoxedCopyFunc) tp_status_spec_copy, + (GBoxedFreeFunc) tp_status_spec_destroy); + } + + return type; +} + +/** + * _tp_status_spec_new: + * @type: The type of a presence. + * @status: The string identifier of this status. + * @may_set_on_self: If true, the user can set this status on themselves using + * tp_connection_set_self_presence. + * @can_have_message: If true, a non-empty message can be set for this status. + * + * + * + * Returns: a newly allocated #TpStatusSpec, free it with + * tp_status_spec_destroy() + * Since: 0.11.UNRELEASED + */ +TpStatusSpec * +_tp_status_spec_new (TpConnectionPresenceType type, + const gchar *status, + gboolean may_set_on_self, + gboolean can_have_message) +{ + TpStatusSpec *self; + + g_return_val_if_fail (status != NULL, NULL); + + self = g_slice_new0 (TpStatusSpec); + self->type = type; + self->status = g_strdup (status); + self->may_set_on_self = may_set_on_self; + self->can_have_message = can_have_message; + + return self; +} + +/** + * tp_status_spec_copy: + * @self: a #TpStatusSpec + * + * + * + * Returns: a newly allocated #TpStatusSpec, free it with + * tp_status_spec_destroy() + * Since: 0.11.UNRELEASED + */ +TpStatusSpec * +tp_status_spec_copy (const TpStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return _tp_status_spec_new (self->type, self->status, self->may_set_on_self, + self->can_have_message); +} + +/** + * tp_status_spec_destroy: + * @self: a #TpStatusSpec + * + * Free all memory used by the #TpStatusSpec. + * + * Since: 0.11.UNRELEASED + */ +void +tp_status_spec_destroy (TpStatusSpec *self) +{ + g_return_if_fail (self != NULL); + + g_free (self->status); + g_slice_free (TpStatusSpec, self); +} + +/** + * tp_connection_get_allowed_presence_statuses: (skip) + * @self: a connection + * + * Return a dictionary of presence statuses valid for use in this connection. + * + * The value may have changed arbitrarily during the time the Connection + * spends in status %TP_CONNECTION_STATUS_CONNECTING, again staying fixed for + * the entire time in %TP_CONNECTION_STATUS_CONNECTED. + * + * This method requires %TP_CONNECTION_FEATURE_SELF_PRESENCE to be enabled. + * To wait for the allowed presence statuses, call tp_proxy_prepare_async() + * with the feature %TP_CONNECTION_FEATURE_SELF_PRESENCE. + * + * Returns: (element-type utf8 TelepathyGLib.StatusSpec) (transfer none): + * Dictionary from string identifiers to structs for each valid status. + * + * Since: 0.11.UNRELEASED + */ +GHashTable * +tp_connection_get_allowed_presence_statuses (TpConnection *self) +{ + g_return_val_if_fail (TP_IS_CONNECTION (self), NULL); + + return self->priv->allowed_presence_statuses; +} + +static void +tp_connection_set_self_presence_cb (TpConnection *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = (GSimpleAsyncResult *) user_data; + + if (error != NULL) + g_simple_async_result_set_from_error (result, error); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +/** + * tp_connection_set_self_presence_async: + * @self: a connection + * @status: the desired status, one of the statuses returned by + * tp_connection_get_allowed_presence_statuses() + * @status_message: desired status message + * @callback: (allow-none): if not %NULL, called exactly once, when the + * operation has been finished or has failed + * @user_data: user-supplied data + * + * Set the self presence status. + * + * Note that clients SHOULD set the status message for the local user to the + * empty string, unless the user has actually provided a specific message (i.e. + * one that conveys more information than the Status). + * + * Since: 0.11.UNRELEASED + */ +void +tp_connection_set_self_presence_async (TpConnection *self, + const gchar *status, + const gchar *status_message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = NULL; + + if (status == NULL) + status = ""; + + if (callback != NULL) + { + result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + tp_connection_set_self_presence_async); + + tp_cli_connection_interface_simple_presence_call_set_presence (self, -1, + status, status_message, tp_connection_set_self_presence_cb, result, + NULL, NULL); + } + else + { + tp_cli_connection_interface_simple_presence_call_set_presence (self, -1, + status, status_message, NULL, NULL, NULL, NULL); + } +} + +/** + * tp_connection_set_self_presence_finish: + * @self: a connection + * @result: the result passed to the callback of + * tp_connection_set_self_presence_async() + * @error: used to return an error if %FALSE is returned + * + * Check for error in a call to tp_connection_set_self_presence_finish(). + * + * Returns: %FALSE (setting @error) if tp_connection_set_self_presence_async() + * failed or was cancelled. + * + * Since: 0.11.UNRELEASED + */ +gboolean +tp_connection_set_self_presence_finish (TpConnection *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail ( + g_simple_async_result_is_valid (result, G_OBJECT (self), + tp_connection_set_self_presence_async), + FALSE); + + return TRUE; +} diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h index d476166..fdb1b11 100644 --- a/telepathy-glib/connection.h +++ b/telepathy-glib/connection.h @@ -83,6 +83,27 @@ struct _TpConnection { TpConnectionPrivate *priv; }; +typedef struct _TpStatusSpec TpStatusSpec; +struct _TpStatusSpec +{ + TpConnectionPresenceType type; + gchar *status; + gboolean may_set_on_self; + gboolean can_have_message; + + /**/ + gpointer priv; +}; + +#define TP_TYPE_STATUS_SPEC (tp_status_spec_get_type ()) +GType tp_status_spec_get_type (void); + +TpStatusSpec *tp_status_spec_copy (const TpStatusSpec *self) + G_GNUC_WARN_UNUSED_RESULT; + +void tp_status_spec_destroy (TpStatusSpec *self); + + GType tp_connection_get_type (void); #define TP_ERRORS_DISCONNECTED (tp_errors_disconnected_quark ()) @@ -171,6 +192,19 @@ GQuark tp_connection_get_feature_quark_capabilities (void) G_GNUC_CONST; (tp_connection_get_feature_quark_avatar_requirements ()) GQuark tp_connection_get_feature_quark_avatar_requirements (void) G_GNUC_CONST; +#define TP_CONNECTION_FEATURE_SELF_PRESENCE \ + (tp_connection_get_feature_quark_self_presence ()) +GQuark tp_connection_get_feature_quark_self_presence (void) G_GNUC_CONST; + +GHashTable *tp_connection_get_allowed_presence_statuses (TpConnection *self); + +void tp_connection_set_self_presence_async (TpConnection *self, + const gchar *status, const gchar *status_message, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_connection_set_self_presence_finish (TpConnection *self, + GAsyncResult *result, GError **error); + /* connection-handles.c */ typedef void (*TpConnectionHoldHandlesCb) (TpConnection *connection, diff --git a/tests/dbus/self-presence.c b/tests/dbus/self-presence.c index a61f27b..0490715 100644 --- a/tests/dbus/self-presence.c +++ b/tests/dbus/self-presence.c @@ -20,6 +20,38 @@ #include "tests/lib/util.h" static void +set_self_presence_offline_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpConnection *client_conn = TP_CONNECTION (source); + GMainLoop *main_loop = user_data; + GError *error = NULL; + + tp_connection_set_self_presence_finish (client_conn, result, &error); + + MYASSERT_SAME_STRING (g_quark_to_string (error->domain), + g_quark_to_string (TP_ERRORS)); + + g_main_loop_quit (main_loop); +} + +static void +set_self_presence_available_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpConnection *client_conn = TP_CONNECTION (source); + GMainLoop *main_loop = user_data; + GError *error = NULL; + + tp_connection_set_self_presence_finish (client_conn, result, &error); + g_assert_no_error (error); + + g_main_loop_quit (main_loop); +} + +static void test_simple_presence (ContactsConnection *service_conn, TpConnection *client_conn) { @@ -27,6 +59,8 @@ test_simple_presence (ContactsConnection *service_conn, GValue *value = NULL; GHashTable *statuses; GValueArray *spec; + TpStatusSpec *status_spec; + GMainLoop *main_loop; MYASSERT (tp_cli_dbus_properties_run_get (client_conn, -1, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, "Statuses", @@ -91,6 +125,59 @@ test_simple_presence (ContactsConnection *service_conn, MYASSERT (tp_cli_connection_interface_simple_presence_run_set_presence ( client_conn, -1, "available", "Here I am", &error, NULL), ""); test_assert_no_error (error); + + statuses = tp_connection_get_allowed_presence_statuses (client_conn); + + status_spec = g_hash_table_lookup (statuses, "available"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_AVAILABLE); + MYASSERT_SAME_STRING (status_spec->status, "available"); + MYASSERT (status_spec->may_set_on_self, ""); + MYASSERT (status_spec->can_have_message, ""); + + status_spec = g_hash_table_lookup (statuses, "busy"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_BUSY); + MYASSERT_SAME_STRING (status_spec->status, "busy"); + MYASSERT (status_spec->may_set_on_self, ""); + MYASSERT (status_spec->can_have_message, ""); + + status_spec = g_hash_table_lookup (statuses, "away"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_AWAY); + MYASSERT_SAME_STRING (status_spec->status, "away"); + MYASSERT (status_spec->may_set_on_self, ""); + MYASSERT (status_spec->can_have_message, ""); + + status_spec = g_hash_table_lookup (statuses, "offline"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_OFFLINE); + MYASSERT_SAME_STRING (status_spec->status, "offline"); + MYASSERT (!status_spec->may_set_on_self, ""); + + status_spec = g_hash_table_lookup (statuses, "unknown"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_UNKNOWN); + MYASSERT_SAME_STRING (status_spec->status, "unknown"); + MYASSERT (!status_spec->may_set_on_self, ""); + + status_spec = g_hash_table_lookup (statuses, "error"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_ERROR); + MYASSERT_SAME_STRING (status_spec->status, "error"); + MYASSERT (!status_spec->may_set_on_self, ""); + + main_loop = g_main_loop_new (NULL, FALSE); + tp_connection_set_self_presence_async ( + client_conn, "offline", "", set_self_presence_offline_cb, + main_loop); + g_main_loop_run (main_loop); + + main_loop = g_main_loop_new (NULL, FALSE); + tp_connection_set_self_presence_async ( + client_conn, "available", "Here I am", set_self_presence_available_cb, + main_loop); + g_main_loop_run (main_loop); } static void @@ -200,6 +287,21 @@ test_complex_presence (ContactsConnection *service_conn, statuses = NULL; } +static void +prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpConnection *client_conn = TP_CONNECTION (source); + GMainLoop *main_loop = user_data; + GError *error = NULL; + + tp_proxy_prepare_finish (client_conn, result, &error); + g_assert_no_error (error); + + g_main_loop_quit (main_loop); +} + int main (int argc, char **argv) @@ -213,6 +315,12 @@ main (int argc, TpConnection *client_conn; guint status; gchar **interfaces; + GHashTable *statuses; + TpStatusSpec *status_spec; + GMainLoop *main_loop; + GQuark features[] = { TP_CONNECTION_FEATURE_CORE, + TP_CONNECTION_FEATURE_SELF_PRESENCE, + 0 }; /* Setup */ @@ -258,6 +366,21 @@ main (int argc, g_assert_cmpuint (status, ==, (guint) TP_CONNECTION_STATUS_DISCONNECTED); test_assert_no_error (error); + /* Check allowed statuses before we're CONNECTED */ + main_loop = g_main_loop_new (NULL, FALSE); + tp_proxy_prepare_async (client_conn, features, prepare_cb, main_loop); + g_main_loop_run (main_loop); + + statuses = tp_connection_get_allowed_presence_statuses (client_conn); + MYASSERT (g_hash_table_size (statuses) == 1, ""); + + status_spec = g_hash_table_lookup (statuses, "offline"); + MYASSERT (status_spec != NULL, ""); + MYASSERT_SAME_UINT (status_spec->type, TP_CONNECTION_PRESENCE_TYPE_OFFLINE); + MYASSERT_SAME_STRING (status_spec->status, "offline"); + MYASSERT (!status_spec->may_set_on_self, ""); + + /* This will put us CONNECTED */ MYASSERT (tp_connection_run_until_ready (client_conn, TRUE, &error, NULL), ""); test_assert_no_error (error); diff --git a/tests/lib/contacts-conn.c b/tests/lib/contacts-conn.c index 84a7c6a..47a7b83 100644 --- a/tests/lib/contacts-conn.c +++ b/tests/lib/contacts-conn.c @@ -315,7 +315,8 @@ my_status_available (GObject *object, { TpBaseConnection *base = TP_BASE_CONNECTION (object); - if (base->status != TP_CONNECTION_STATUS_CONNECTED) + if (index != CONTACTS_CONNECTION_STATUS_OFFLINE && + base->status != TP_CONNECTION_STATUS_CONNECTED) return FALSE; return TRUE; -- 1.6.6.1