summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2010-12-09 17:49:59 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2010-12-09 18:00:39 +0000
commitb3849101053ef40575894e83329f4a3ec7a8a83c (patch)
tree4d7bba611ae8dc7f8b9087a390ec3d174333b985
parent57dd9e80adbddaaabc86d40a261b684564592360 (diff)
parent473111247b63bf96762169a4413ffa517a708142 (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.txt1
-rw-r--r--telepathy-glib/connection-internal.h4
-rw-r--r--telepathy-glib/connection.c167
-rw-r--r--telepathy-glib/connection.h4
-rw-r--r--telepathy-glib/contact.c21
-rw-r--r--telepathy-glib/contact.h2
-rw-r--r--tests/dbus/self-handle.c380
-rw-r--r--tests/lib/bug-19101-conn.c27
-rw-r--r--tests/lib/simple-conn.c69
-rw-r--r--tests/lib/simple-conn.h6
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), &times);
+ 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__ */