diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-12-09 17:49:59 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-12-09 18:00:39 +0000 |
commit | b3849101053ef40575894e83329f4a3ec7a8a83c (patch) | |
tree | 4d7bba611ae8dc7f8b9087a390ec3d174333b985 | |
parent | 57dd9e80adbddaaabc86d40a261b684564592360 (diff) | |
parent | 473111247b63bf96762169a4413ffa517a708142 (diff) |
Merge branch 'self-contact'
Reviewed-by: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=32234
-rw-r--r-- | docs/reference/telepathy-glib-sections.txt | 1 | ||||
-rw-r--r-- | telepathy-glib/connection-internal.h | 4 | ||||
-rw-r--r-- | telepathy-glib/connection.c | 167 | ||||
-rw-r--r-- | telepathy-glib/connection.h | 4 | ||||
-rw-r--r-- | telepathy-glib/contact.c | 21 | ||||
-rw-r--r-- | telepathy-glib/contact.h | 2 | ||||
-rw-r--r-- | tests/dbus/self-handle.c | 380 | ||||
-rw-r--r-- | tests/lib/bug-19101-conn.c | 27 | ||||
-rw-r--r-- | tests/lib/simple-conn.c | 69 | ||||
-rw-r--r-- | tests/lib/simple-conn.h | 6 |
10 files changed, 581 insertions, 100 deletions
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 3747d8d67..06d20d8f8 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -3375,6 +3375,7 @@ TpConnectionWhenReadyCb tp_connection_call_when_ready tp_connection_is_ready tp_connection_get_status +tp_connection_get_self_contact tp_connection_get_self_handle tp_connection_has_immortal_handles TpConnectionRequestHandlesCb diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h index 43b14f866..812beeeff 100644 --- a/telepathy-glib/connection-internal.h +++ b/telepathy-glib/connection-internal.h @@ -35,7 +35,8 @@ struct _TpConnectionPrivate { /* list of TpConnectionProc */ GList *introspect_needed; - TpHandle self_handle; + TpHandle last_known_self_handle; + TpContact *self_contact; TpConnectionStatus status; TpConnectionStatusReason status_reason; gchar *connection_error; @@ -78,6 +79,7 @@ struct _TpConnectionPrivate { unsigned tracking_contact_info_changed:1; unsigned introspecting_after_connected:1; unsigned tracking_client_types_updated:1; + unsigned introspecting_self_contact:1; }; void _tp_connection_status_reason_to_gerror (TpConnectionStatusReason reason, diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c index 07e6490fc..a979c9be7 100644 --- a/telepathy-glib/connection.c +++ b/telepathy-glib/connection.c @@ -124,6 +124,7 @@ tp_connection_get_feature_quark_core (void) * <listitem>#TpConnection:status is * %TP_CONNECTION_STATUS_CONNECTED</listitem> * <listitem>#TpConnection:self-handle is valid and non-zero</listitem> + * <listitem>#TpConnection:self-contact is non-%NULL</listitem> * <listitem>all interfaces have been added to the set of * #TpProxy:interfaces, and that set will not change again</listitem> * </itemizedlist> @@ -234,6 +235,7 @@ enum PROP_STATUS = 1, PROP_STATUS_REASON, PROP_CONNECTION_READY, + PROP_SELF_CONTACT, PROP_SELF_HANDLE, PROP_CAPABILITIES, N_PROPS @@ -262,8 +264,11 @@ tp_connection_get_property (GObject *object, case PROP_STATUS_REASON: g_value_set_uint (value, self->priv->status_reason); break; + case PROP_SELF_CONTACT: + g_value_set_object (value, tp_connection_get_self_contact (self)); + break; case PROP_SELF_HANDLE: - g_value_set_uint (value, self->priv->self_handle); + g_value_set_uint (value, tp_connection_get_self_handle (self)); break; case PROP_CAPABILITIES: g_value_set_object (value, self->priv->capabilities); @@ -483,14 +488,79 @@ introspect_contacts (TpConnection *self) } static void -_tp_connection_set_self_handle (TpConnection *self, - guint self_handle) +tp_connection_set_self_contact (TpConnection *self, + TpContact *contact) { - if (self_handle != self->priv->self_handle) + if (contact != self->priv->self_contact) { - self->priv->self_handle = self_handle; + TpContact *tmp = self->priv->self_contact; + + self->priv->self_contact = g_object_ref (contact); + tp_clear_object (&tmp); + g_object_notify ((GObject *) self, "self-contact"); g_object_notify ((GObject *) self, "self-handle"); } + + if (self->priv->introspecting_self_contact) + { + self->priv->introspecting_self_contact = FALSE; + tp_connection_continue_introspection (self); + } +} + +static void +tp_connection_got_self_contact_cb (TpConnection *self, + guint n_contacts, + TpContact * const *contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer unused_data G_GNUC_UNUSED, + GObject *unused_object G_GNUC_UNUSED) +{ + if (n_contacts != 0) + { + g_assert (n_contacts == 1); + g_assert (n_failed == 0); + g_assert (error == NULL); + + if (tp_contact_get_handle (contacts[0]) == + self->priv->last_known_self_handle) + { + tp_connection_set_self_contact (self, contacts[0]); + } + else + { + DEBUG ("SelfHandle is now %u, ignoring contact object for %u", + self->priv->last_known_self_handle, + tp_contact_get_handle (contacts[0])); + } + } + else if (error != NULL) + { + /* Unrecoverable error: we were probably invalidated, but in case + * we weren't... */ + DEBUG ("Failed to hold the handle from GetSelfHandle(): %s", + error->message); + tp_proxy_invalidate ((TpProxy *) self, error); + } + else if (n_failed == 1 && failed[0] != self->priv->last_known_self_handle) + { + /* Since we tried to make the TpContact, our self-handle has changed, + * so it doesn't matter that we couldn't make a TpContact for the old + * one - carry on and make a TpContact for the new one instead. */ + DEBUG ("Failed to hold handle %u from GetSelfHandle(), but it's " + "changed to %u anyway, so never mind", failed[0], + self->priv->last_known_self_handle); + } + else + { + GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "The handle from GetSelfHandle() was considered invalid" }; + + DEBUG ("%s", e.message); + tp_proxy_invalidate ((TpProxy *) self, &e); + } } static void @@ -506,12 +576,25 @@ got_self_handle (TpConnection *self, if (error != NULL) { DEBUG ("%p: GetSelfHandle() failed: %s", self, error->message); - self_handle = 0; - /* FIXME: abort the readying process */ + tp_proxy_invalidate ((TpProxy *) self, error); + return; } - _tp_connection_set_self_handle (self, self_handle); - tp_connection_continue_introspection (self); + if (self_handle == 0) + { + GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT, + "GetSelfHandle() returned 0" }; + DEBUG ("%s", e.message); + tp_proxy_invalidate ((TpProxy *) self, &e); + return; + } + + self->priv->last_known_self_handle = self_handle; + self->priv->introspecting_self_contact = TRUE; + /* This relies on the special case in tp_connection_get_contacts_by_handle() + * which makes it start working slightly early. */ + tp_connection_get_contacts_by_handle (self, 1, &self_handle, 0, NULL, + tp_connection_got_self_contact_cb, NULL, NULL, NULL); } static void @@ -520,7 +603,16 @@ on_self_handle_changed (TpConnection *self, gpointer user_data G_GNUC_UNUSED, GObject *user_object G_GNUC_UNUSED) { - _tp_connection_set_self_handle (self, self_handle); + if (self_handle == 0) + { + DEBUG ("Ignoring alleged change of self-handle to %u", self_handle); + return; + } + + DEBUG ("SelfHandleChanged to %u, I wonder what that means?", self_handle); + self->priv->last_known_self_handle = self_handle; + tp_connection_get_contacts_by_handle (self, 1, &self_handle, 0, NULL, + tp_connection_got_self_contact_cb, NULL, NULL, NULL); } static void @@ -874,8 +966,6 @@ tp_connection_invalidated (TpConnection *self) tp_proxy_pending_call_cancel (self->priv->introspection_call); self->priv->introspection_call = NULL; } - - _tp_connection_set_self_handle (self, 0); } static void @@ -1146,6 +1236,10 @@ tp_connection_class_init (TpConnectionClass *klass) * The %TP_HANDLE_TYPE_CONTACT handle of the local user on this connection, * or 0 if we don't know yet or if the connection has become invalid. * + * This may change if the local user's unique identifier changes (for + * instance by using /nick on IRC), in which case #GObject::notify will be + * emitted. + * * To wait for a valid self-handle (and other properties), call * tp_proxy_prepare_async() with the feature * %TP_CONNECTION_FEATURE_CONNECTED. @@ -1158,6 +1252,28 @@ tp_connection_class_init (TpConnectionClass *klass) param_spec); /** + * TpConnection:self-contact: + * + * A #TpContact representing the local user on this connection, + * or %NULL if not yet available. + * + * If the local user's unique identifier changes (for instance by using + * /nick on IRC), this property will change to a different #TpContact object + * representing the new identifier, and #GObject::notify will be emitted. + * + * To wait for a non-%NULL self-contact (and other properties), call + * tp_proxy_prepare_async() with the feature + * %TP_CONNECTION_FEATURE_CONNECTED. + * + * Since: 0.13.UNRELEASED + */ + param_spec = g_param_spec_object ("self-contact", "Self contact", + "The local user's Contact object on this connection", TP_TYPE_CONTACT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SELF_CONTACT, + param_spec); + + /** * TpConnection:status-reason: * * To wait for a valid status (and other properties), call @@ -1315,7 +1431,11 @@ TpHandle tp_connection_get_self_handle (TpConnection *self) { g_return_val_if_fail (TP_IS_CONNECTION (self), 0); - return self->priv->self_handle; + + if (self->priv->self_contact == NULL) + return 0; + + return tp_contact_get_handle (self->priv->self_contact); } /** @@ -2182,3 +2302,24 @@ tp_connection_has_immortal_handles (TpConnection *self) return self->priv->has_immortal_handles; } + +/** + * tp_connection_get_self_contact: + * @self: a connection + * + * Return a #TpContact representing the local user on this connection. + * + * The returned object is not necessarily valid after the main loop is + * re-entered; ref it with g_object_ref() if you want to keep it. + * + * Returns: (transfer none): the value of the TpConnection:self-contact + * property, which may be %NULL + * + * Since: 0.13.UNRELEASED + */ +TpContact * +tp_connection_get_self_contact (TpConnection *self) +{ + g_return_val_if_fail (TP_IS_CONNECTION (self), NULL); + return self->priv->self_contact; +} diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h index 8d8d3734f..1463023cf 100644 --- a/telepathy-glib/connection.h +++ b/telepathy-glib/connection.h @@ -74,6 +74,9 @@ GType tp_contact_info_list_get_type (void); GList *tp_contact_info_list_copy (GList *list); void tp_contact_info_list_free (GList *list); +/* forward declaration, see contact.h for the rest */ +typedef struct _TpContact TpContact; + typedef struct _TpConnection TpConnection; typedef struct _TpConnectionPrivate TpConnectionPrivate; typedef struct _TpConnectionClass TpConnectionClass; @@ -124,6 +127,7 @@ TpConnectionStatus tp_connection_get_status (TpConnection *self, TpConnectionStatusReason *reason); TpHandle tp_connection_get_self_handle (TpConnection *self); +TpContact *tp_connection_get_self_contact (TpConnection *self); TpCapabilities * tp_connection_get_capabilities (TpConnection *self); diff --git a/telepathy-glib/contact.c b/telepathy-glib/contact.c index 1aa79f12d..31904132d 100644 --- a/telepathy-glib/contact.c +++ b/telepathy-glib/contact.c @@ -2922,7 +2922,8 @@ contacts_context_supports_iface (ContactsContext *context, TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)) return FALSE; - g_assert (contact_attribute_interfaces != NULL); + if (contact_attribute_interfaces == NULL) + return FALSE; for (i = 0; i < contact_attribute_interfaces->len; i++) { @@ -3177,6 +3178,10 @@ contacts_get_attributes (ContactsContext *context) GPtrArray *array; const gchar **supported_interfaces; guint i; + guint len = 0; + + if (contact_attribute_interfaces != NULL) + len = contact_attribute_interfaces->len; /* tp_connection_get_contact_attributes insists that you have at least one * handle; skip it if we don't (can only happen if we started from IDs) */ @@ -3188,11 +3193,10 @@ contacts_get_attributes (ContactsContext *context) g_assert (tp_proxy_has_interface_by_id (context->connection, TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS)); - g_assert (contact_attribute_interfaces != NULL); - array = g_ptr_array_sized_new (contact_attribute_interfaces->len); + array = g_ptr_array_sized_new (len); - for (i = 0; i < contact_attribute_interfaces->len; i++) + for (i = 0; i < len; i++) { GQuark q = g_array_index (contact_attribute_interfaces, GQuark, i); @@ -3386,7 +3390,14 @@ tp_connection_get_contacts_by_handle (TpConnection *self, GPtrArray *contacts; guint i; - g_return_if_fail (tp_connection_is_ready (self)); + /* As an implementation detail, this method actually starts working slightly + * before we're officially ready, but only if you don't want any features. + * We use this to get the TpContact for the SelfHandle. */ + if (n_features != 0 || !self->priv->introspecting_after_connected) + { + g_return_if_fail (tp_connection_is_ready (self)); + } + g_return_if_fail (tp_proxy_get_invalidated (self) == NULL); g_return_if_fail (n_handles >= 1); g_return_if_fail (handles != NULL); diff --git a/telepathy-glib/contact.h b/telepathy-glib/contact.h index 708d84187..0176be1f6 100644 --- a/telepathy-glib/contact.h +++ b/telepathy-glib/contact.h @@ -32,7 +32,7 @@ G_BEGIN_DECLS -typedef struct _TpContact TpContact; +/* TpContact is forward-declared in connection.h */ typedef struct _TpContactClass TpContactClass; typedef struct _TpContactPrivate TpContactPrivate; diff --git a/tests/dbus/self-handle.c b/tests/dbus/self-handle.c index a0bc2fb5f..a48949244 100644 --- a/tests/dbus/self-handle.c +++ b/tests/dbus/self-handle.c @@ -1,6 +1,6 @@ -/* Feature test for the user's self-handle changing. +/* Feature test for the user's self-handle/self-contact changing. * - * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/> * Copyright (C) 2009 Nokia Corporation * * Copying and distribution of this file, with or without modification, @@ -19,10 +19,66 @@ #include "tests/lib/myassert.h" #include "tests/lib/util.h" +typedef struct { + TpDBusDaemon *dbus; + TpTestsSimpleConnection *service_conn; + TpBaseConnection *service_conn_as_base; + gchar *name; + gchar *conn_path; + GError *error /* zero-initialized */; + TpConnection *client_conn; + TpHandleRepoIface *contact_repo; + GAsyncResult *result; +} Fixture; + +static void +setup (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) +{ + gboolean ok; + + f->dbus = tp_tests_dbus_daemon_dup_or_die (); + + f->service_conn = TP_TESTS_SIMPLE_CONNECTION ( + tp_tests_object_new_static_class (TP_TESTS_TYPE_SIMPLE_CONNECTION, + "account", "me@example.com", + "protocol", "simple", + NULL)); + f->service_conn_as_base = TP_BASE_CONNECTION (f->service_conn); + g_object_ref (f->service_conn_as_base); + g_assert (f->service_conn != NULL); + g_assert (f->service_conn_as_base != NULL); + + f->contact_repo = tp_base_connection_get_handles (f->service_conn_as_base, + TP_HANDLE_TYPE_CONTACT); + + ok = tp_base_connection_register (f->service_conn_as_base, "simple", + &f->name, &f->conn_path, &f->error); + g_assert_no_error (f->error); + g_assert (ok); + + f->client_conn = tp_connection_new (f->dbus, f->name, f->conn_path, + &f->error); + g_assert_no_error (f->error); + g_assert (f->client_conn != NULL); +} + static void -on_self_handle_changed (TpConnection *client_conn, - GParamSpec *param_spec G_GNUC_UNUSED, - gpointer user_data) +setup_and_connect (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) +{ + gboolean ok; + + setup (f, unused); + + ok = tp_connection_run_until_ready (f->client_conn, TRUE, &f->error, NULL); + g_assert_no_error (f->error); + g_assert (ok); +} + +/* we'll get more arguments, but just ignore them */ +static void +swapped_counter_cb (gpointer user_data) { guint *times = user_data; @@ -30,108 +86,284 @@ on_self_handle_changed (TpConnection *client_conn, } static void -test_self_handle (TpTestsSimpleConnection *service_conn, - TpConnection *client_conn) +test_self_handle (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) { - TpBaseConnection *service_conn_as_base = TP_BASE_CONNECTION (service_conn); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - service_conn_as_base, TP_HANDLE_TYPE_CONTACT); TpHandle handle; - guint times = 0; + TpContact *before, *after; + guint handle_times = 0, contact_times = 0; - g_signal_connect (client_conn, "notify::self-handle", - G_CALLBACK (on_self_handle_changed), ×); + g_signal_connect_swapped (f->client_conn, "notify::self-handle", + G_CALLBACK (swapped_counter_cb), &handle_times); + g_signal_connect_swapped (f->client_conn, "notify::self-contact", + G_CALLBACK (swapped_counter_cb), &contact_times); - g_assert_cmpstr (tp_handle_inspect (contact_repo, - tp_base_connection_get_self_handle (service_conn_as_base)), ==, + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, "me@example.com"); - g_assert_cmpuint (tp_connection_get_self_handle (client_conn), ==, - tp_base_connection_get_self_handle (service_conn_as_base)); + g_assert_cmpuint (tp_connection_get_self_handle (f->client_conn), ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); - g_object_get (client_conn, + g_object_get (f->client_conn, "self-handle", &handle, + "self-contact", &before, NULL); g_assert_cmpuint (handle, ==, - tp_base_connection_get_self_handle (service_conn_as_base)); + tp_base_connection_get_self_handle (f->service_conn_as_base)); + g_assert_cmpuint (tp_contact_get_handle (before), ==, handle); + g_assert_cmpstr (tp_contact_get_identifier (before), ==, "me@example.com"); - g_assert_cmpuint (times, ==, 0); + g_assert_cmpuint (handle_times, ==, 0); + g_assert_cmpuint (contact_times, ==, 0); /* similar to /nick in IRC */ - tp_tests_simple_connection_set_identifier (service_conn, "myself@example.org"); - tp_tests_proxy_run_until_dbus_queue_processed (client_conn); - g_assert_cmpuint (times, ==, 1); + tp_tests_simple_connection_set_identifier (f->service_conn, + "myself@example.org"); + tp_tests_proxy_run_until_dbus_queue_processed (f->client_conn); - g_assert_cmpstr (tp_handle_inspect (contact_repo, - tp_base_connection_get_self_handle (service_conn_as_base)), ==, + while (handle_times < 1 || contact_times < 1) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (handle_times, ==, 1); + g_assert_cmpuint (contact_times, ==, 1); + + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, "myself@example.org"); - g_assert_cmpuint (tp_connection_get_self_handle (client_conn), ==, - tp_base_connection_get_self_handle (service_conn_as_base)); + g_assert_cmpuint (tp_connection_get_self_handle (f->client_conn), ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); - g_object_get (client_conn, + g_object_get (f->client_conn, "self-handle", &handle, + "self-contact", &after, NULL); + g_assert (before != after); g_assert_cmpuint (handle, ==, - tp_base_connection_get_self_handle (service_conn_as_base)); + tp_base_connection_get_self_handle (f->service_conn_as_base)); + g_assert_cmpuint (tp_contact_get_handle (after), ==, handle); + g_assert_cmpstr (tp_contact_get_identifier (after), ==, + "myself@example.org"); + + g_object_unref (before); + g_object_unref (after); } -int -main (int argc, - char **argv) +static void +test_change_early (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) { - TpDBusDaemon *dbus; - TpTestsSimpleConnection *service_conn; - TpBaseConnection *service_conn_as_base; - gchar *name; - gchar *conn_path; - GError *error = NULL; - TpConnection *client_conn; + TpHandle handle; + TpContact *after; + guint handle_times = 0, contact_times = 0; + gboolean ok; + GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; - /* Setup */ + g_signal_connect_swapped (f->client_conn, "notify::self-handle", + G_CALLBACK (swapped_counter_cb), &handle_times); + g_signal_connect_swapped (f->client_conn, "notify::self-contact", + G_CALLBACK (swapped_counter_cb), &contact_times); - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - dbus = tp_tests_dbus_daemon_dup_or_die (); + tp_proxy_prepare_async (f->client_conn, features, tp_tests_result_ready_cb, + &f->result); + g_assert (f->result == NULL); - service_conn = TP_TESTS_SIMPLE_CONNECTION (tp_tests_object_new_static_class ( - TP_TESTS_TYPE_SIMPLE_CONNECTION, - "account", "me@example.com", - "protocol", "simple", - NULL)); - service_conn_as_base = TP_BASE_CONNECTION (service_conn); - MYASSERT (service_conn != NULL, ""); - MYASSERT (service_conn_as_base != NULL, ""); + /* act as though someone else called Connect; emit signals in quick + * succession, so that by the time the TpConnection tries to investigate + * the self-handle, it has already changed */ + tp_base_connection_change_status (f->service_conn_as_base, + TP_CONNECTION_STATUS_CONNECTING, + TP_CONNECTION_STATUS_REASON_REQUESTED); + tp_tests_simple_connection_set_identifier (f->service_conn, + "me@example.com"); + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, + "me@example.com"); + tp_base_connection_change_status (f->service_conn_as_base, + TP_CONNECTION_STATUS_CONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); + tp_tests_simple_connection_set_identifier (f->service_conn, + "myself@example.org"); + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, + "myself@example.org"); - MYASSERT (tp_base_connection_register (service_conn_as_base, "simple", - &name, &conn_path, &error), ""); - g_assert_no_error (error); + /* now run the main loop and let the client catch up */ + tp_tests_run_until_result (&f->result); + ok = tp_proxy_prepare_finish (f->client_conn, f->result, &f->error); + g_assert_no_error (f->error); + g_assert (ok); - client_conn = tp_connection_new (dbus, name, conn_path, &error); - MYASSERT (client_conn != NULL, ""); - g_assert_no_error (error); - MYASSERT (tp_connection_run_until_ready (client_conn, TRUE, &error, NULL), - ""); - g_assert_no_error (error); + /* the self-handle and self-contact change once during connection */ + g_assert_cmpuint (handle_times, ==, 1); + g_assert_cmpuint (contact_times, ==, 1); - /* Tests */ + g_assert_cmpuint (tp_connection_get_self_handle (f->client_conn), ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); - test_self_handle (service_conn, client_conn); + g_object_get (f->client_conn, + "self-handle", &handle, + "self-contact", &after, + NULL); + g_assert_cmpuint (handle, ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); + g_assert_cmpuint (tp_contact_get_handle (after), ==, handle); + g_assert_cmpstr (tp_contact_get_identifier (after), ==, + "myself@example.org"); + + g_object_unref (after); +} + +static void +test_change_inconveniently (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) +{ + TpHandle handle; + TpContact *after; + guint handle_times = 0, contact_times = 0, got_self_handle_times = 0; + gboolean ok; + GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; + + g_signal_connect_swapped (f->client_conn, "notify::self-handle", + G_CALLBACK (swapped_counter_cb), &handle_times); + g_signal_connect_swapped (f->client_conn, "notify::self-contact", + G_CALLBACK (swapped_counter_cb), &contact_times); + g_signal_connect_swapped (f->service_conn, "got-self-handle", + G_CALLBACK (swapped_counter_cb), &got_self_handle_times); + + tp_proxy_prepare_async (f->client_conn, features, tp_tests_result_ready_cb, + &f->result); + g_assert (f->result == NULL); + + /* act as though someone else called Connect */ + tp_base_connection_change_status (f->service_conn_as_base, + TP_CONNECTION_STATUS_CONNECTING, + TP_CONNECTION_STATUS_REASON_REQUESTED); + tp_tests_simple_connection_set_identifier (f->service_conn, + "me@example.com"); + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, + "me@example.com"); + tp_base_connection_change_status (f->service_conn_as_base, + TP_CONNECTION_STATUS_CONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); + + /* run the main loop until just after GetSelfHandle is processed, to make + * sure the client first saw the old self handle */ + while (got_self_handle_times == 0) + g_main_context_iteration (NULL, TRUE); + + tp_tests_simple_connection_set_identifier (f->service_conn, + "myself@example.org"); + g_assert_cmpstr (tp_handle_inspect (f->contact_repo, + tp_base_connection_get_self_handle (f->service_conn_as_base)), ==, + "myself@example.org"); + + /* now run the main loop and let the client catch up */ + tp_tests_run_until_result (&f->result); + ok = tp_proxy_prepare_finish (f->client_conn, f->result, &f->error); + g_assert_no_error (f->error); + g_assert (ok); - /* Teardown */ + /* the self-handle and self-contact change once during connection */ + g_assert_cmpuint (handle_times, ==, 1); + g_assert_cmpuint (contact_times, ==, 1); - MYASSERT (tp_cli_connection_run_disconnect (client_conn, -1, &error, NULL), - ""); - g_assert_no_error (error); - g_object_unref (client_conn); + g_assert_cmpuint (tp_connection_get_self_handle (f->client_conn), ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); - service_conn_as_base = NULL; - g_object_unref (service_conn); - g_free (name); - g_free (conn_path); + g_object_get (f->client_conn, + "self-handle", &handle, + "self-contact", &after, + NULL); + g_assert_cmpuint (handle, ==, + tp_base_connection_get_self_handle (f->service_conn_as_base)); + g_assert_cmpuint (tp_contact_get_handle (after), ==, handle); + g_assert_cmpstr (tp_contact_get_identifier (after), ==, + "myself@example.org"); + + g_object_unref (after); +} + +static void +test_self_handle_fails (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) +{ + GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; + gboolean ok; + + tp_proxy_prepare_async (f->client_conn, features, tp_tests_result_ready_cb, + &f->result); + g_assert (f->result == NULL); + + tp_tests_simple_connection_set_identifier (f->service_conn, + "me@example.com"); + tp_tests_simple_connection_set_get_self_handle_error (f->service_conn, + TP_ERROR, TP_ERROR_CONFUSED, "totally wasted"); + tp_base_connection_change_status (f->service_conn_as_base, + TP_CONNECTION_STATUS_CONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); + + /* now run the main loop and let the client catch up */ + tp_tests_run_until_result (&f->result); + ok = tp_proxy_prepare_finish (f->client_conn, f->result, &f->error); + g_assert_error (f->error, TP_ERROR, TP_ERROR_CONFUSED); + g_assert (!ok); + g_clear_error (&f->error); + + g_assert_error (tp_proxy_get_invalidated (f->client_conn), TP_ERROR, + TP_ERROR_CONFUSED); + + /* don't want to Disconnect during teardown - it'll just fail */ + tp_tests_simple_connection_inject_disconnect (f->service_conn); + tp_clear_object (&f->client_conn); +} + +static void +teardown (Fixture *f, + gconstpointer unused G_GNUC_UNUSED) +{ + gboolean ok; + + g_clear_error (&f->error); + + if (f->client_conn != NULL) + { + ok = tp_cli_connection_run_disconnect (f->client_conn, -1, &f->error, + NULL); + g_assert_no_error (f->error); + g_assert (ok); + } + + tp_clear_object (&f->result); + tp_clear_object (&f->client_conn); + tp_clear_object (&f->service_conn_as_base); + tp_clear_object (&f->service_conn); + tp_clear_pointer (&f->name, g_free); + tp_clear_pointer (&f->conn_path, g_free); + tp_clear_object (&f->dbus); +} + +int +main (int argc, + char **argv) +{ + tp_tests_abort_after (10); + g_type_init (); + tp_debug_set_flags ("all"); + g_set_prgname ("self-handle"); + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); - g_object_unref (dbus); + g_test_add ("/self-handle", Fixture, NULL, setup_and_connect, + test_self_handle, teardown); + g_test_add ("/self-handle/change-early", Fixture, NULL, setup, + test_change_early, teardown); + g_test_add ("/self-handle/change-inconveniently", Fixture, NULL, setup, + test_change_inconveniently, teardown); + g_test_add ("/self-handle/fails", Fixture, NULL, setup, + test_self_handle_fails, teardown); - return 0; + return g_test_run (); } diff --git a/tests/lib/bug-19101-conn.c b/tests/lib/bug-19101-conn.c index d112476b2..6f06fe9f0 100644 --- a/tests/lib/bug-19101-conn.c +++ b/tests/lib/bug-19101-conn.c @@ -11,6 +11,8 @@ #include "bug-19101-conn.h" +#include <telepathy-glib/interfaces.h> + #include "debug.h" static void contacts_iface_init (gpointer g_iface, gpointer iface_data); @@ -32,7 +34,7 @@ tp_tests_bug19101_connection_class_init (TpTestsBug19101ConnectionClass *klass) } /* A broken implementation of GetContactAttributes, which returns an empty dict - * of attributes for each handle. + * of attributes for each handle (other than the self-handle). */ static void tp_tests_bug19101_connection_get_contact_attributes ( @@ -42,12 +44,30 @@ tp_tests_bug19101_connection_get_contact_attributes ( gboolean hold, DBusGMethodInvocation *context) { - GHashTable *result = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) g_hash_table_destroy); + TpBaseConnection *base_conn = TP_BASE_CONNECTION (iface); + GHashTable *result; guint i; + const gchar *assumed_interfaces[] = { + TP_IFACE_CONNECTION, + NULL + }; + + if (handles->len == 1 && + g_array_index (handles, TpHandle, 0) == base_conn->self_handle) + { + DEBUG ("called for self-handle (during preparation), not being rubbish"); + /* strictly speaking this should hold the handles on behalf of the + * sending process, but handles are immortal now anyway... */ + result = tp_contacts_mixin_get_contact_attributes ((GObject *) iface, + handles, interfaces, assumed_interfaces, NULL); + goto finally; + } DEBUG ("called; returning rubbish"); + result = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_hash_table_destroy); + for (i = 0 ; i < handles->len ; i++) { TpHandle h= g_array_index (handles, TpHandle, i); @@ -56,6 +76,7 @@ tp_tests_bug19101_connection_get_contact_attributes ( g_hash_table_insert (result, GUINT_TO_POINTER(h), attr_hash); } +finally: tp_svc_connection_interface_contacts_return_from_get_contact_attributes ( context, result); g_hash_table_unref (result); diff --git a/tests/lib/simple-conn.c b/tests/lib/simple-conn.c index 8a35f823d..e5c3b68ed 100644 --- a/tests/lib/simple-conn.c +++ b/tests/lib/simple-conn.c @@ -1,7 +1,7 @@ /* * simple-conn.c - a simple connection * - * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2010 Collabora Ltd. <http://www.collabora.co.uk/> * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, @@ -25,8 +25,11 @@ #include "textchan-null.h" #include "util.h" -G_DEFINE_TYPE (TpTestsSimpleConnection, tp_tests_simple_connection, - TP_TYPE_BASE_CONNECTION); +static void conn_iface_init (TpSvcConnectionClass *); + +G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleConnection, tp_tests_simple_connection, + TP_TYPE_BASE_CONNECTION, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, conn_iface_init)) /* type definition stuff */ @@ -36,6 +39,14 @@ enum N_PROPS }; +enum +{ + SIGNAL_GOT_SELF_HANDLE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = {0}; + struct _TpTestsSimpleConnectionPrivate { gchar *account; @@ -44,6 +55,8 @@ struct _TpTestsSimpleConnectionPrivate /* TpHandle => reffed TpTestsTextChannelNull */ GHashTable *channels; + + GError *get_self_handle_error /* initially NULL */ ; }; static void @@ -116,6 +129,7 @@ finalize (GObject *object) g_source_remove (self->priv->disconnect_source); } + g_clear_error (&self->priv->get_self_handle_error); g_free (self->priv->account); G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->finalize (object); @@ -267,6 +281,14 @@ tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec); + + signals[SIGNAL_GOT_SELF_HANDLE] = g_signal_new ("got-self-handle", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } void @@ -345,3 +367,44 @@ tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self, return chan_path; } + +void +tp_tests_simple_connection_set_get_self_handle_error ( + TpTestsSimpleConnection *self, + GQuark domain, + gint code, + const gchar *message) +{ + self->priv->get_self_handle_error = g_error_new_literal (domain, code, + message); +} + +static void +get_self_handle (TpSvcConnection *iface, + DBusGMethodInvocation *context) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (iface); + TpBaseConnection *base = TP_BASE_CONNECTION (iface); + + g_assert (TP_IS_BASE_CONNECTION (base)); + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (self->priv->get_self_handle_error != NULL) + { + dbus_g_method_return_error (context, self->priv->get_self_handle_error); + return; + } + + tp_svc_connection_return_from_get_self_handle (context, base->self_handle); + g_signal_emit (self, signals[SIGNAL_GOT_SELF_HANDLE], 0); +} + +static void +conn_iface_init (TpSvcConnectionClass *iface) +{ +#define IMPLEMENT(prefix,x) \ + tp_svc_connection_implement_##x (iface, prefix##x) + IMPLEMENT(,get_self_handle); +#undef IMPLEMENT +} diff --git a/tests/lib/simple-conn.h b/tests/lib/simple-conn.h index 1d65238c7..6322f4b86 100644 --- a/tests/lib/simple-conn.h +++ b/tests/lib/simple-conn.h @@ -66,6 +66,12 @@ gchar * tp_tests_simple_connection_ensure_text_chan ( const gchar *target_id, GHashTable **props); +void tp_tests_simple_connection_set_get_self_handle_error ( + TpTestsSimpleConnection *self, + GQuark domain, + gint code, + const gchar *message); + G_END_DECLS #endif /* #ifndef __TP_TESTS_SIMPLE_CONN_H__ */ |