diff options
author | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2011-01-19 12:53:59 -0500 |
---|---|---|
committer | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2011-01-19 16:24:40 -0500 |
commit | a94c5e5cbc5c12e531ea677f1b7fba701e244f16 (patch) | |
tree | b68c6a3b0f61dc95982886a26cfcff29770ec43f | |
parent | 26d77d70c6f7f52efb2dd7a829416015d87ee26c (diff) |
Add test for _tpl_entity_new_from_tp_contact()
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/lib/Makefile.am | 26 | ||||
-rw-r--r-- | tests/lib/contacts-conn.c | 1268 | ||||
-rw-r--r-- | tests/lib/contacts-conn.h | 184 | ||||
-rw-r--r-- | tests/lib/simple-conn.c | 413 | ||||
-rw-r--r-- | tests/lib/simple-conn.h | 77 | ||||
-rw-r--r-- | tests/lib/textchan-null.c | 570 | ||||
-rw-r--r-- | tests/lib/textchan-null.h | 137 | ||||
-rw-r--r-- | tests/lib/util.c | 291 | ||||
-rw-r--r-- | tests/lib/util.h | 57 | ||||
-rw-r--r-- | tests/suppressions/tpl.supp | 46 | ||||
-rw-r--r-- | tests/test-entity.c | 106 |
13 files changed, 3167 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac index f39cc73..f1efa76 100644 --- a/configure.ac +++ b/configure.ac @@ -222,6 +222,7 @@ AC_CONFIG_FILES([ telepathy-logger/telepathy-logger-0.1-uninstalled.pc tools/Makefile tests/Makefile + tests/lib/Makefile tests/suppressions/Makefile tests/twisted/Makefile tests/twisted/tools/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 6db1d88..b09613a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,7 @@ if WANT_TWISTED_TESTS CHECKTWISTED = twisted endif -SUBDIRS = $(CHECKTWISTED) suppressions +SUBDIRS = $(CHECKTWISTED) suppressions lib LDADD = \ $(top_builddir)/telepathy-logger/libtelepathy-logger.la \ @@ -31,6 +31,10 @@ test_searches_SOURCES = \ test-searches.c \ constants.h +test_entity_LDADD = \ + $(top_builddir)/tests/lib/libtp-logger-tests.la \ + $(LDADD) + AM_CFLAGS = \ $(ERROR_CFLAGS) \ $(TPL_CFLAGS) \ diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am new file mode 100644 index 0000000..97bcf88 --- /dev/null +++ b/tests/lib/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libtp-logger-tests.la + +libtp_logger_tests_la_SOURCES = \ + contacts-conn.c \ + contacts-conn.h \ + simple-conn.c \ + simple-conn.h \ + textchan-null.c \ + textchan-null.h \ + util.c \ + util.h + +check_c_sources = *.c +include $(top_srcdir)/tools/check-coding-style.mk +check-local: check-coding-style + +AM_CFLAGS = \ + $(ERROR_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(TPL_CFLAGS) + +libtp_logger_tests_la_LIBADD = \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(top_builddir)/telepathy-logger/libtelepathy-logger.la diff --git a/tests/lib/contacts-conn.c b/tests/lib/contacts-conn.c new file mode 100644 index 0000000..f786d52 --- /dev/null +++ b/tests/lib/contacts-conn.c @@ -0,0 +1,1268 @@ +/* + * contacts-conn.c - connection with contact info + * + * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ +#include "contacts-conn.h" + +#include <dbus/dbus-glib.h> + +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/errors.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/handle-repo-dynamic.h> +#include <telepathy-glib/util.h> + +static void init_aliasing (gpointer, gpointer); +static void init_avatars (gpointer, gpointer); +static void init_location (gpointer, gpointer); +static void init_contact_caps (gpointer, gpointer); +static void init_contact_info (gpointer, gpointer); +static void conn_avatars_properties_getter (GObject *object, GQuark interface, + GQuark name, GValue *value, gpointer getter_data); + +G_DEFINE_TYPE_WITH_CODE (TpTestsContactsConnection, + tp_tests_contacts_connection, + TP_TESTS_TYPE_SIMPLE_CONNECTION, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, + init_aliasing); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, + init_avatars); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, + tp_contacts_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, + tp_presence_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + tp_presence_mixin_simple_presence_iface_init) + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION, + init_location) + G_IMPLEMENT_INTERFACE ( + TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, + init_contact_caps) + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO, + init_contact_info) + ); + +/* type definition stuff */ + +static const char *mime_types[] = { "image/png", NULL }; +static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = { + { "MinimumAvatarWidth", GUINT_TO_POINTER (1), NULL }, + { "MinimumAvatarHeight", GUINT_TO_POINTER (2), NULL }, + { "RecommendedAvatarWidth", GUINT_TO_POINTER (3), NULL }, + { "RecommendedAvatarHeight", GUINT_TO_POINTER (4), NULL }, + { "MaximumAvatarWidth", GUINT_TO_POINTER (5), NULL }, + { "MaximumAvatarHeight", GUINT_TO_POINTER (6), NULL }, + { "MaximumAvatarBytes", GUINT_TO_POINTER (7), NULL }, + /* special-cased - it's the only one with a non-guint value */ + { "SupportedAvatarMIMETypes", NULL, NULL }, + { NULL } +}; + +enum +{ + N_SIGNALS +}; + +struct _TpTestsContactsConnectionPrivate +{ + /* TpHandle => gchar * */ + GHashTable *aliases; + /* TpHandle => AvatarData */ + GHashTable *avatars; + /* TpHandle => ContactsConnectionPresenceStatusIndex */ + GHashTable *presence_statuses; + /* TpHandle => gchar * */ + GHashTable *presence_messages; + /* TpHandle => GHashTable * */ + GHashTable *locations; + /* TpHandle => GPtrArray * */ + GHashTable *capabilities; + /* TpHandle => GPtrArray * */ + GHashTable *contact_info; + GPtrArray *default_contact_info; +}; + +typedef struct +{ + GArray *data; + gchar *mime_type; + gchar *token; +} AvatarData; + +static AvatarData * +avatar_data_new (GArray *data, + const gchar *mime_type, + const gchar *token) +{ + AvatarData *a; + + a = g_slice_new (AvatarData); + a->data = data ? g_array_ref (data) : NULL; + a->mime_type = g_strdup (mime_type); + a->token = g_strdup (token); + + return a; +} + +static void +avatar_data_free (gpointer data) +{ + AvatarData *a = data; + + if (a != NULL) + { + if (a->data != NULL) + g_array_unref (a->data); + g_free (a->mime_type); + g_free (a->token); + g_slice_free (AvatarData, a); + } +} + +static void +free_rcc_list (GPtrArray *rccs) +{ + g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs); +} + +static void +tp_tests_contacts_connection_init (TpTestsContactsConnection *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_CONTACTS_CONNECTION, + TpTestsContactsConnectionPrivate); + self->priv->aliases = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, g_free); + self->priv->avatars = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, avatar_data_free); + self->priv->presence_statuses = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, NULL); + self->priv->presence_messages = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, g_free); + self->priv->locations = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_hash_table_unref); + self->priv->capabilities = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) free_rcc_list); + self->priv->contact_info = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); +} + +static void +finalize (GObject *object) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + + tp_contacts_mixin_finalize (object); + g_hash_table_destroy (self->priv->aliases); + g_hash_table_destroy (self->priv->avatars); + g_hash_table_destroy (self->priv->presence_statuses); + g_hash_table_destroy (self->priv->presence_messages); + g_hash_table_destroy (self->priv->locations); + g_hash_table_destroy (self->priv->capabilities); + g_hash_table_destroy (self->priv->contact_info); + + if (self->priv->default_contact_info != NULL) + g_ptr_array_unref (self->priv->default_contact_info); + + G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->finalize (object); +} + +static void +aliasing_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + guint i; + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + TpBaseConnection *base = TP_BASE_CONNECTION (object); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + const gchar *alias = g_hash_table_lookup (self->priv->aliases, + GUINT_TO_POINTER (handle)); + + if (alias == NULL) + { + alias = tp_handle_inspect (contact_repo, handle); + } + + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias", + tp_g_value_slice_new_string (alias)); + } +} + +static void +avatars_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + guint i; + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + AvatarData *a = g_hash_table_lookup (self->priv->avatars, + GUINT_TO_POINTER (handle)); + + if (a != NULL && a->token != NULL) + { + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_AVATARS "/token", + tp_g_value_slice_new_string (a->token)); + } + } +} + +static void +location_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + guint i; + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + GHashTable *location = g_hash_table_lookup (self->priv->locations, + GUINT_TO_POINTER (handle)); + + if (location != NULL) + { + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_LOCATION "/location", + tp_g_value_slice_new_boxed (TP_HASH_TYPE_LOCATION, location)); + } + } +} + +static void +contact_caps_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + guint i; + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + GPtrArray *caps = g_hash_table_lookup (self->priv->capabilities, + GUINT_TO_POINTER (handle)); + + if (caps != NULL) + { + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES "/capabilities", + tp_g_value_slice_new_boxed ( + TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps)); + } + } +} + +static void +contact_info_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + guint i; + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + GPtrArray *info = g_hash_table_lookup (self->priv->contact_info, + GUINT_TO_POINTER (handle)); + + if (info != NULL) + { + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO "/info", + tp_g_value_slice_new_boxed (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, + info)); + } + } +} + +static TpDBusPropertiesMixinPropImpl conn_contact_info_properties[] = { + { "ContactInfoFlags", GUINT_TO_POINTER (TP_CONTACT_INFO_FLAG_PUSH | + TP_CONTACT_INFO_FLAG_CAN_SET), NULL }, + { "SupportedFields", NULL, NULL }, + { NULL } +}; + +static void +conn_contact_info_properties_getter (GObject *object, + GQuark interface, + GQuark name, + GValue *value, + gpointer getter_data) +{ + GQuark q_supported_fields = g_quark_from_static_string ("SupportedFields"); + static GPtrArray *supported_fields = NULL; + + if (name == q_supported_fields) + { + if (supported_fields == NULL) + { + supported_fields = g_ptr_array_new (); + g_ptr_array_add (supported_fields, tp_value_array_build (4, + G_TYPE_STRING, "n", + G_TYPE_STRV, NULL, + G_TYPE_UINT, 0, + G_TYPE_UINT, 0, + G_TYPE_INVALID)); + } + g_value_set_boxed (value, supported_fields); + } + else + { + g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); + } +} + +static void +constructed (GObject *object) +{ + TpBaseConnection *base = TP_BASE_CONNECTION (object); + void (*parent_impl) (GObject *) = + G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->constructed; + + if (parent_impl != NULL) + parent_impl (object); + + tp_contacts_mixin_init (object, + G_STRUCT_OFFSET (TpTestsContactsConnection, contacts_mixin)); + tp_base_connection_register_with_contacts_mixin (base); + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + aliasing_fill_contact_attributes); + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_AVATARS, + avatars_fill_contact_attributes); + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_LOCATION, + location_fill_contact_attributes); + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, + contact_caps_fill_contact_attributes); + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, + contact_info_fill_contact_attributes); + + tp_presence_mixin_init (object, + G_STRUCT_OFFSET (TpTestsContactsConnection, presence_mixin)); + tp_presence_mixin_simple_presence_register_with_contacts_mixin (object); +} + +static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = { + { "message", "s", NULL, NULL }, + { NULL } +}; + +/* Must match TpTestsContactsConnectionPresenceStatusIndex in the .h */ +static const TpPresenceStatusSpec my_statuses[] = { + { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, + can_have_message }, + { "busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, can_have_message }, + { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, can_have_message }, + { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL }, + { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL }, + { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, NULL }, + { NULL } +}; + +static gboolean +my_status_available (GObject *object, + guint index) +{ + TpBaseConnection *base = TP_BASE_CONNECTION (object); + + if (base->status != TP_CONNECTION_STATUS_CONNECTED) + return FALSE; + + return TRUE; +} + +static GHashTable * +my_get_contact_statuses (GObject *object, + const GArray *contacts, + GError **error) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); + GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) tp_presence_status_free); + guint i; + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + gpointer key = GUINT_TO_POINTER (handle); + TpTestsContactsConnectionPresenceStatusIndex index; + const gchar *presence_message; + GHashTable *parameters; + + index = GPOINTER_TO_UINT (g_hash_table_lookup ( + self->priv->presence_statuses, key)); + presence_message = g_hash_table_lookup ( + self->priv->presence_messages, key); + + parameters = g_hash_table_new_full (g_str_hash, + g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); + + if (presence_message != NULL) + g_hash_table_insert (parameters, "message", + tp_g_value_slice_new_string (presence_message)); + + g_hash_table_insert (result, key, + tp_presence_status_new (index, parameters)); + g_hash_table_destroy (parameters); + } + + return result; +} + +static gboolean +my_set_own_status (GObject *object, + const TpPresenceStatus *status, + GError **error) +{ + TpBaseConnection *base_conn = TP_BASE_CONNECTION (object); + TpTestsContactsConnectionPresenceStatusIndex index = status->index; + const gchar *message = ""; + + if (status->optional_arguments != NULL) + { + message = g_hash_table_lookup (status->optional_arguments, "message"); + + if (message == NULL) + message = ""; + } + + tp_tests_contacts_connection_change_presences (TP_TESTS_CONTACTS_CONNECTION (object), + 1, &(base_conn->self_handle), &index, &message); + + return TRUE; +} + +static void +tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass) +{ + TpBaseConnectionClass *base_class = + (TpBaseConnectionClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + TP_IFACE_CONNECTION_INTERFACE_AVATARS, + TP_IFACE_CONNECTION_INTERFACE_CONTACTS, + TP_IFACE_CONNECTION_INTERFACE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_LOCATION, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + NULL }; + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + { TP_IFACE_CONNECTION_INTERFACE_AVATARS, + conn_avatars_properties_getter, + NULL, + conn_avatars_properties, + }, + { TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, + conn_contact_info_properties_getter, + NULL, + conn_contact_info_properties, + }, + { NULL } + }; + + object_class->constructed = constructed; + object_class->finalize = finalize; + g_type_class_add_private (klass, sizeof (TpTestsContactsConnectionPrivate)); + + base_class->interfaces_always_present = interfaces_always_present; + + tp_contacts_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsContactsConnectionClass, contacts_mixin)); + + tp_presence_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsContactsConnectionClass, presence_mixin), + my_status_available, my_get_contact_statuses, + my_set_own_status, my_statuses); + + tp_presence_mixin_simple_presence_init_dbus_properties (object_class); + + klass->properties_class.interfaces = prop_interfaces; + tp_dbus_properties_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsContactsConnectionClass, properties_class)); +} + +void +tp_tests_contacts_connection_change_aliases (TpTestsContactsConnection *self, + guint n, + const TpHandle *handles, + const gchar * const *aliases) +{ + GPtrArray *structs = g_ptr_array_sized_new (n); + guint i; + + for (i = 0; i < n; i++) + { + GValueArray *pair = g_value_array_new (2); + + g_hash_table_insert (self->priv->aliases, + GUINT_TO_POINTER (handles[i]), g_strdup (aliases[i])); + + g_value_array_append (pair, NULL); + g_value_init (pair->values + 0, G_TYPE_UINT); + g_value_set_uint (pair->values + 0, handles[i]); + + g_value_array_append (pair, NULL); + g_value_init (pair->values + 1, G_TYPE_STRING); + g_value_set_string (pair->values + 1, aliases[i]); + + g_ptr_array_add (structs, pair); + } + + tp_svc_connection_interface_aliasing_emit_aliases_changed (self, + structs); + + g_ptr_array_foreach (structs, (GFunc) g_value_array_free, NULL); + g_ptr_array_free (structs, TRUE); +} + +void +tp_tests_contacts_connection_change_presences ( + TpTestsContactsConnection *self, + guint n, + const TpHandle *handles, + const TpTestsContactsConnectionPresenceStatusIndex *indexes, + const gchar * const *messages) +{ + GHashTable *presences = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) tp_presence_status_free); + guint i; + + for (i = 0; i < n; i++) + { + GHashTable *parameters; + gpointer key = GUINT_TO_POINTER (handles[i]); + + g_hash_table_insert (self->priv->presence_statuses, key, + GUINT_TO_POINTER (indexes[i])); + g_hash_table_insert (self->priv->presence_messages, key, + g_strdup (messages[i])); + + parameters = g_hash_table_new_full (g_str_hash, + g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); + + if (messages[i] != NULL && messages[i][0] != '\0') + g_hash_table_insert (parameters, "message", + tp_g_value_slice_new_string (messages[i])); + + g_hash_table_insert (presences, key, tp_presence_status_new (indexes[i], + parameters)); + g_hash_table_destroy (parameters); + } + + tp_presence_mixin_emit_presence_update ((GObject *) self, + presences); + g_hash_table_destroy (presences); +} + +void +tp_tests_contacts_connection_change_avatar_tokens (TpTestsContactsConnection *self, + guint n, + const TpHandle *handles, + const gchar * const *tokens) +{ + guint i; + + for (i = 0; i < n; i++) + { + g_hash_table_insert (self->priv->avatars, + GUINT_TO_POINTER (handles[i]), avatar_data_new (NULL, NULL, tokens[i])); + tp_svc_connection_interface_avatars_emit_avatar_updated (self, + handles[i], tokens[i]); + } +} + +void +tp_tests_contacts_connection_change_avatar_data ( + TpTestsContactsConnection *self, + TpHandle handle, + GArray *data, + const gchar *mime_type, + const gchar *token) +{ + g_hash_table_insert (self->priv->avatars, + GUINT_TO_POINTER (handle), avatar_data_new (data, mime_type, token)); + + tp_svc_connection_interface_avatars_emit_avatar_updated (self, + handle, token); +} + +void +tp_tests_contacts_connection_change_locations (TpTestsContactsConnection *self, + guint n, + const TpHandle *handles, + GHashTable **locations) +{ + guint i; + + for (i = 0; i < n; i++) + { + tp_asv_dump (locations[i]); + g_hash_table_insert (self->priv->locations, + GUINT_TO_POINTER (handles[i]), g_hash_table_ref (locations[i])); + + tp_svc_connection_interface_location_emit_location_updated (self, + handles[i], locations[i]); + } +} + +void +tp_tests_contacts_connection_change_capabilities ( + TpTestsContactsConnection *self, + GHashTable *capabilities) +{ + GHashTableIter iter; + gpointer handle, caps; + + g_hash_table_iter_init (&iter, capabilities); + while (g_hash_table_iter_next (&iter, &handle, &caps)) + { + g_hash_table_insert (self->priv->capabilities, + handle, + g_boxed_copy (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, + caps)); + } + + tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed ( + self, capabilities); +} + +void +tp_tests_contacts_connection_change_contact_info ( + TpTestsContactsConnection *self, + TpHandle handle, + GPtrArray *info) +{ + g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), + g_ptr_array_ref (info)); + + tp_svc_connection_interface_contact_info_emit_contact_info_changed (self, + handle, info); +} + +void +tp_tests_contacts_connection_set_default_contact_info ( + TpTestsContactsConnection *self, + GPtrArray *info) +{ + if (self->priv->default_contact_info != NULL) + g_ptr_array_unref (self->priv->default_contact_info); + self->priv->default_contact_info = g_ptr_array_ref (info); +} + +static void +my_get_alias_flags (TpSvcConnectionInterfaceAliasing *aliasing, + DBusGMethodInvocation *context) +{ + TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context, + 0); +} + +static void +my_get_aliases (TpSvcConnectionInterfaceAliasing *aliasing, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); + TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GHashTable *result; + GError *error = NULL; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + const gchar *alias = g_hash_table_lookup (self->priv->aliases, + GUINT_TO_POINTER (handle)); + + if (alias == NULL) + g_hash_table_insert (result, GUINT_TO_POINTER (handle), + (gchar *) tp_handle_inspect (contact_repo, handle)); + else + g_hash_table_insert (result, GUINT_TO_POINTER (handle), + (gchar *) alias); + } + + tp_svc_connection_interface_aliasing_return_from_get_aliases (context, + result); + g_hash_table_destroy (result); +} + +static void +my_request_aliases (TpSvcConnectionInterfaceAliasing *aliasing, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); + TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GPtrArray *result; + gchar **strings; + GError *error = NULL; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_ptr_array_sized_new (contacts->len + 1); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + const gchar *alias = g_hash_table_lookup (self->priv->aliases, + GUINT_TO_POINTER (handle)); + + if (alias == NULL) + g_ptr_array_add (result, + (gchar *) tp_handle_inspect (contact_repo, handle)); + else + g_ptr_array_add (result, (gchar *) alias); + } + + g_ptr_array_add (result, NULL); + strings = (gchar **) g_ptr_array_free (result, FALSE); + tp_svc_connection_interface_aliasing_return_from_request_aliases (context, + (const gchar **) strings); + g_free (strings); +} + +static void +init_aliasing (gpointer g_iface, + gpointer iface_data) +{ + TpSvcConnectionInterfaceAliasingClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\ + klass, my_##x) + IMPLEMENT(get_alias_flags); + IMPLEMENT(request_aliases); + IMPLEMENT(get_aliases); + /* IMPLEMENT(set_aliases); */ +#undef IMPLEMENT +} + +static void +my_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); + TpBaseConnection *base = TP_BASE_CONNECTION (avatars); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + GHashTable *result; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + AvatarData *a = g_hash_table_lookup (self->priv->avatars, + GUINT_TO_POINTER (handle)); + + if (a == NULL || a->token == NULL) + { + /* we're expected to do a round-trip to the server to find out + * their token, so we have to give some sort of result. Assume + * no avatar, here */ + a = avatar_data_new (NULL, NULL, ""); + g_hash_table_insert (self->priv->avatars, + GUINT_TO_POINTER (handle), a); + tp_svc_connection_interface_avatars_emit_avatar_updated (self, + handle, a->token); + } + + g_hash_table_insert (result, GUINT_TO_POINTER (handle), + a->token); + } + + tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( + context, result); + g_hash_table_destroy (result); +} + +static void +my_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); + TpBaseConnection *base = TP_BASE_CONNECTION (avatars); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + GHashTable *result; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + AvatarData *a = g_hash_table_lookup (self->priv->avatars, + GUINT_TO_POINTER (handle)); + const gchar *token = a ? a->token : NULL; + + g_hash_table_insert (result, GUINT_TO_POINTER (handle), + (gchar *) (token != NULL ? token : "")); + } + + tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( + context, result); + g_hash_table_destroy (result); +} + +static void +my_request_avatars (TpSvcConnectionInterfaceAvatars *avatars, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); + TpBaseConnection *base = TP_BASE_CONNECTION (avatars); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + AvatarData *a = g_hash_table_lookup (self->priv->avatars, + GUINT_TO_POINTER (handle)); + + if (a != NULL) + tp_svc_connection_interface_avatars_emit_avatar_retrieved (self, handle, + a->token, a->data, a->mime_type); + } + + tp_svc_connection_interface_avatars_return_from_request_avatars (context); +} + +static void +conn_avatars_properties_getter (GObject *object, + GQuark interface, + GQuark name, + GValue *value, + gpointer getter_data) +{ + GQuark q_mime_types = g_quark_from_static_string ( + "SupportedAvatarMIMETypes"); + + if (name == q_mime_types) + { + g_value_set_static_boxed (value, mime_types); + } + else + { + g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); + } +} + +static void +init_avatars (gpointer g_iface, + gpointer iface_data) +{ + TpSvcConnectionInterfaceAvatarsClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\ + klass, my_##x) + /* IMPLEMENT(get_avatar_requirements); */ + IMPLEMENT(get_avatar_tokens); + IMPLEMENT(get_known_avatar_tokens); + /* IMPLEMENT(request_avatar); */ + IMPLEMENT(request_avatars); + /* IMPLEMENT(set_avatar); */ + /* IMPLEMENT(clear_avatar); */ +#undef IMPLEMENT +} + +static void +my_get_locations (TpSvcConnectionInterfaceLocation *avatars, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); + TpBaseConnection *base = TP_BASE_CONNECTION (avatars); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + GHashTable *result; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + GHashTable *location = g_hash_table_lookup (self->priv->locations, + GUINT_TO_POINTER (handle)); + + if (location != NULL) + { + g_hash_table_insert (result, GUINT_TO_POINTER (handle), location); + } + } + + tp_svc_connection_interface_location_return_from_get_locations ( + context, result); + g_hash_table_destroy (result); +} + +static void +init_location (gpointer g_iface, + gpointer iface_data) +{ + TpSvcConnectionInterfaceLocationClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_location_implement_##x (\ + klass, my_##x) + IMPLEMENT(get_locations); +#undef IMPLEMENT +} + +static void +my_get_contact_capabilities (TpSvcConnectionInterfaceContactCapabilities *obj, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); + TpBaseConnection *base = TP_BASE_CONNECTION (obj); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + GHashTable *result; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + GPtrArray *arr = g_hash_table_lookup (self->priv->capabilities, + GUINT_TO_POINTER (handle)); + + if (arr != NULL) + { + g_hash_table_insert (result, GUINT_TO_POINTER (handle), arr); + } + } + + tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities ( + context, result); + + g_hash_table_destroy (result); +} + +static void +init_contact_caps (gpointer g_iface, + gpointer iface_data) +{ + TpSvcConnectionInterfaceContactCapabilitiesClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_contact_capabilities_implement_##x (\ + klass, my_##x) + IMPLEMENT(get_contact_capabilities); +#undef IMPLEMENT +} + +static GPtrArray * +lookup_contact_info (TpTestsContactsConnection *self, + TpHandle handle) +{ + GPtrArray *ret = g_hash_table_lookup (self->priv->contact_info, + GUINT_TO_POINTER (handle)); + + if (ret == NULL && self->priv->default_contact_info != NULL) + { + ret = self->priv->default_contact_info; + g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), + g_ptr_array_ref (ret)); + } + + return ret; +} + +static void +my_refresh_contact_info (TpSvcConnectionInterfaceContactInfo *obj, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); + TpBaseConnection *base = TP_BASE_CONNECTION (obj); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, guint, i); + GPtrArray *arr = lookup_contact_info (self, handle); + + tp_svc_connection_interface_contact_info_emit_contact_info_changed (self, + handle, arr); + } + + tp_svc_connection_interface_contact_info_return_from_refresh_contact_info ( + context); +} + +static void +my_request_contact_info (TpSvcConnectionInterfaceContactInfo *obj, + guint handle, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); + TpBaseConnection *base = TP_BASE_CONNECTION (obj); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + GPtrArray *ret; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handle_is_valid (contact_repo, handle, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + ret = lookup_contact_info (self, handle); + + tp_svc_connection_interface_contact_info_return_from_request_contact_info ( + context, ret); +} + +static void +my_set_contact_info (TpSvcConnectionInterfaceContactInfo *obj, + const GPtrArray *info, + DBusGMethodInvocation *context) +{ + TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); + TpBaseConnection *base = TP_BASE_CONNECTION (obj); + GPtrArray *copy; + guint i; + TpHandle self_handle; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + /* Deep copy info */ + copy = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free); + for (i = 0; i < info->len; i++) + g_ptr_array_add (copy, g_value_array_copy (g_ptr_array_index (info, i))); + + self_handle = tp_base_connection_get_self_handle (base); + g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (self_handle), + copy); + + tp_svc_connection_interface_contact_info_return_from_set_contact_info ( + context); +} + +static void +init_contact_info (gpointer g_iface, + gpointer iface_data) +{ + TpSvcConnectionInterfaceContactInfoClass *klass = g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x (\ + klass, my_##x) + IMPLEMENT (refresh_contact_info); + IMPLEMENT (request_contact_info); + IMPLEMENT (set_contact_info); +#undef IMPLEMENT +} + +/* =============== Legacy version (no Contacts interface) ================= */ + +G_DEFINE_TYPE (TpTestsLegacyContactsConnection, + tp_tests_legacy_contacts_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION); + +enum +{ + LEGACY_PROP_HAS_IMMORTAL_HANDLES = 1 +}; + +static void +legacy_contacts_connection_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + case LEGACY_PROP_HAS_IMMORTAL_HANDLES: + /* Pretend we don't. */ + g_value_set_boolean (value, FALSE); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tp_tests_legacy_contacts_connection_init (TpTestsLegacyContactsConnection *self) +{ +} + +static void +tp_tests_legacy_contacts_connection_class_init ( + TpTestsLegacyContactsConnectionClass *klass) +{ + /* Leave Contacts out of the interfaces we say are present, so clients + * won't use it */ + static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + TP_IFACE_CONNECTION_INTERFACE_AVATARS, + TP_IFACE_CONNECTION_INTERFACE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_LOCATION, + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + NULL }; + TpBaseConnectionClass *base_class = + (TpBaseConnectionClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->get_property = legacy_contacts_connection_get_property; + + base_class->interfaces_always_present = interfaces_always_present; + + g_object_class_override_property (object_class, + LEGACY_PROP_HAS_IMMORTAL_HANDLES, "has-immortal-handles"); +} + +/* =============== No Requests and no ContactCapabilities ================= */ + +G_DEFINE_TYPE (TpTestsNoRequestsConnection, tp_tests_no_requests_connection, + TP_TESTS_TYPE_CONTACTS_CONNECTION); + +static void +tp_tests_no_requests_connection_init (TpTestsNoRequestsConnection *self) +{ +} + +static void +tp_tests_no_requests_connection_class_init ( + TpTestsNoRequestsConnectionClass *klass) +{ + static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + TP_IFACE_CONNECTION_INTERFACE_AVATARS, + TP_IFACE_CONNECTION_INTERFACE_CONTACTS, + TP_IFACE_CONNECTION_INTERFACE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_LOCATION, + NULL }; + TpBaseConnectionClass *base_class = + (TpBaseConnectionClass *) klass; + + base_class->interfaces_always_present = interfaces_always_present; +} diff --git a/tests/lib/contacts-conn.h b/tests/lib/contacts-conn.h new file mode 100644 index 0000000..baab460 --- /dev/null +++ b/tests/lib/contacts-conn.h @@ -0,0 +1,184 @@ +/* + * contacts-conn.h - header for a connection with contact info + * + * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef __TP_TESTS_CONTACTS_CONN_H__ +#define __TP_TESTS_CONTACTS_CONN_H__ + +#include <glib-object.h> +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/contacts-mixin.h> +#include <telepathy-glib/presence-mixin.h> + +#include "simple-conn.h" + +G_BEGIN_DECLS + +typedef struct _TpTestsContactsConnection TpTestsContactsConnection; +typedef struct _TpTestsContactsConnectionClass TpTestsContactsConnectionClass; +typedef struct _TpTestsContactsConnectionPrivate TpTestsContactsConnectionPrivate; + +struct _TpTestsContactsConnectionClass { + TpTestsSimpleConnectionClass parent_class; + + TpPresenceMixinClass presence_mixin; + TpContactsMixinClass contacts_mixin; + TpDBusPropertiesMixinClass properties_class; +}; + +struct _TpTestsContactsConnection { + TpTestsSimpleConnection parent; + + TpPresenceMixin presence_mixin; + TpContactsMixin contacts_mixin; + + TpTestsContactsConnectionPrivate *priv; +}; + +GType tp_tests_contacts_connection_get_type (void); + +/* Must match my_statuses in the .c */ +typedef enum { + TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE, + TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY, + TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY, + TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE, + TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN, + TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR +} TpTestsContactsConnectionPresenceStatusIndex; + +/* TYPE MACROS */ +#define TP_TESTS_TYPE_CONTACTS_CONNECTION \ + (tp_tests_contacts_connection_get_type ()) +#define TP_TESTS_CONTACTS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ + TpTestsContactsConnection)) +#define TP_TESTS_CONTACTS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ + TpTestsContactsConnectionClass)) +#define TP_TESTS_IS_CONTACTS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION)) +#define TP_TESTS_IS_CONTACTS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_CONTACTS_CONNECTION)) +#define TP_TESTS_CONTACTS_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ + TpTestsContactsConnectionClass)) + +void tp_tests_contacts_connection_change_aliases ( + TpTestsContactsConnection *self, guint n, + const TpHandle *handles, const gchar * const *aliases); + +void tp_tests_contacts_connection_change_presences ( + TpTestsContactsConnection *self, guint n, const TpHandle *handles, + const TpTestsContactsConnectionPresenceStatusIndex *indexes, + const gchar * const *messages); + +void tp_tests_contacts_connection_change_avatar_tokens ( + TpTestsContactsConnection *self, guint n, const TpHandle *handles, + const gchar * const *tokens); + +void tp_tests_contacts_connection_change_avatar_data ( + TpTestsContactsConnection *self, + TpHandle handle, + GArray *data, + const gchar *mime_type, + const gchar *token); + +void tp_tests_contacts_connection_change_locations ( + TpTestsContactsConnection *self, + guint n, + const TpHandle *handles, + GHashTable **locations); + +void tp_tests_contacts_connection_change_capabilities ( + TpTestsContactsConnection *self, GHashTable *capabilities); + +void tp_tests_contacts_connection_change_contact_info ( + TpTestsContactsConnection *self, TpHandle handle, GPtrArray *info); + +void tp_tests_contacts_connection_set_default_contact_info ( + TpTestsContactsConnection *self, + GPtrArray *info); + +/* Legacy version (no Contacts interface, and no immortal handles) */ + +typedef struct _TpTestsLegacyContactsConnection TpTestsLegacyContactsConnection; +typedef struct _TpTestsLegacyContactsConnectionClass TpTestsLegacyContactsConnectionClass; +typedef struct _TpTestsLegacyContactsConnectionPrivate + TpTestsLegacyContactsConnectionPrivate; + +struct _TpTestsLegacyContactsConnectionClass { + TpTestsContactsConnectionClass parent_class; +}; + +struct _TpTestsLegacyContactsConnection { + TpTestsContactsConnection parent; + + TpTestsLegacyContactsConnectionPrivate *priv; +}; + +GType tp_tests_legacy_contacts_connection_get_type (void); + +/* TYPE MACROS */ +#define TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION \ + (tp_tests_legacy_contacts_connection_get_type ()) +#define LEGACY_TP_TESTS_CONTACTS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ + TpTestsLegacyContactsConnection)) +#define LEGACY_TP_TESTS_CONTACTS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ + TpTestsLegacyContactsConnectionClass)) +#define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION)) +#define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION)) +#define LEGACY_TP_TESTS_CONTACTS_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ + TpTestsLegacyContactsConnectionClass)) + +/* No Requests version */ + +typedef struct _TpTestsNoRequestsConnection TpTestsNoRequestsConnection; +typedef struct _TpTestsNoRequestsConnectionClass TpTestsNoRequestsConnectionClass; +typedef struct _TpTestsNoRequestsConnectionPrivate + TpTestsNoRequestsConnectionPrivate; + +struct _TpTestsNoRequestsConnectionClass { + TpTestsContactsConnectionClass parent_class; +}; + +struct _TpTestsNoRequestsConnection { + TpTestsContactsConnection parent; + + TpTestsNoRequestsConnectionPrivate *priv; +}; + +GType tp_tests_no_requests_connection_get_type (void); + +/* TYPE MACROS */ +#define TP_TESTS_TYPE_NO_REQUESTS_CONNECTION \ + (tp_tests_no_requests_connection_get_type ()) +#define TP_TESTS_NO_REQUESTS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ + TpTestsNoRequestsConnection)) +#define TP_TESTS_NO_REQUESTS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ + TpTestsNoRequestsConnectionClass)) +#define TP_TESTS_NO_REQUESTS_IS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION)) +#define TP_TESTS_NO_REQUESTS_IS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION)) +#define TP_TESTS_NO_REQUESTS_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ + TpTestsNoRequestsConnectionClass)) + +G_END_DECLS + +#endif /* ifndef __TP_TESTS_CONTACTS_CONN_H__ */ diff --git a/tests/lib/simple-conn.c b/tests/lib/simple-conn.c new file mode 100644 index 0000000..cbe3eda --- /dev/null +++ b/tests/lib/simple-conn.c @@ -0,0 +1,413 @@ +/* + * simple-conn.c - a simple connection + * + * 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, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#include "simple-conn.h" + +#include <string.h> + +#include <dbus/dbus-glib.h> + +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/errors.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/handle-repo-dynamic.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/util.h> + +#include "textchan-null.h" +#include "util.h" + +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 */ + +enum +{ + PROP_ACCOUNT = 1, + N_PROPS +}; + +enum +{ + SIGNAL_GOT_SELF_HANDLE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = {0}; + +struct _TpTestsSimpleConnectionPrivate +{ + gchar *account; + guint connect_source; + guint disconnect_source; + + /* TpHandle => reffed TpTestsTextChannelNull */ + GHashTable *channels; + + GError *get_self_handle_error /* initially NULL */ ; +}; + +static void +tp_tests_simple_connection_init (TpTestsSimpleConnection *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TESTS_TYPE_SIMPLE_CONNECTION, TpTestsSimpleConnectionPrivate); + + self->priv->channels = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_object_unref); +} + +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *spec) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); + + switch (property_id) { + case PROP_ACCOUNT: + g_value_set_string (value, self->priv->account); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); + } +} + +static void +set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *spec) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); + + switch (property_id) { + case PROP_ACCOUNT: + g_free (self->priv->account); + self->priv->account = g_utf8_strdown (g_value_get_string (value), -1); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); + } +} + +static void +dispose (GObject *object) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); + + g_hash_table_unref (self->priv->channels); + + G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); + + if (self->priv->connect_source != 0) + { + g_source_remove (self->priv->connect_source); + } + + if (self->priv->disconnect_source != 0) + { + 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); +} + +static gchar * +get_unique_connection_name (TpBaseConnection *conn) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); + + return g_strdup (self->priv->account); +} + +static gchar * +tp_tests_simple_normalize_contact (TpHandleRepoIface *repo, + const gchar *id, + gpointer context, + GError **error) +{ + if (id[0] == '\0') + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE, + "ID must not be empty"); + return NULL; + } + + if (strchr (id, ' ') != NULL) + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE, + "ID must not contain spaces"); + return NULL; + } + + return g_utf8_strdown (id, -1); +} + +static void +create_handle_repos (TpBaseConnection *conn, + TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) +{ + repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new + (TP_HANDLE_TYPE_CONTACT, tp_tests_simple_normalize_contact, NULL); + repos[TP_HANDLE_TYPE_ROOM] = tp_dynamic_handle_repo_new + (TP_HANDLE_TYPE_ROOM, NULL, NULL); +} + +static GPtrArray * +create_channel_factories (TpBaseConnection *conn) +{ + return g_ptr_array_sized_new (0); +} + +void +tp_tests_simple_connection_inject_disconnect (TpTestsSimpleConnection *self) +{ + tp_base_connection_change_status ((TpBaseConnection *) self, + TP_CONNECTION_STATUS_DISCONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); +} + +static gboolean +pretend_connected (gpointer data) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); + TpBaseConnection *conn = (TpBaseConnection *) self; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, + TP_HANDLE_TYPE_CONTACT); + + conn->self_handle = tp_handle_ensure (contact_repo, self->priv->account, + NULL, NULL); + + if (conn->status == TP_CONNECTION_STATUS_CONNECTING) + { + tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); + } + + self->priv->connect_source = 0; + return FALSE; +} + +static gboolean +start_connecting (TpBaseConnection *conn, + GError **error) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); + + tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTING, + TP_CONNECTION_STATUS_REASON_REQUESTED); + + /* In a real connection manager we'd ask the underlying implementation to + * start connecting, then go to state CONNECTED when finished. Here there + * isn't actually a connection, so we'll fake a connection process that + * takes time. */ + self->priv->connect_source = g_timeout_add (0, pretend_connected, self); + + return TRUE; +} + +static gboolean +pretend_disconnected (gpointer data) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); + + /* We are disconnected, all our channels are invalidated */ + g_hash_table_remove_all (self->priv->channels); + + tp_base_connection_finish_shutdown (TP_BASE_CONNECTION (data)); + self->priv->disconnect_source = 0; + return FALSE; +} + +static void +shut_down (TpBaseConnection *conn) +{ + TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); + + /* In a real connection manager we'd ask the underlying implementation to + * start shutting down, then call this function when finished. Here there + * isn't actually a connection, so we'll fake a disconnection process that + * takes time. */ + self->priv->disconnect_source = g_timeout_add (0, pretend_disconnected, + conn); +} + +static void +tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass) +{ + TpBaseConnectionClass *base_class = + (TpBaseConnectionClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + GParamSpec *param_spec; + static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL }; + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + g_type_class_add_private (klass, sizeof (TpTestsSimpleConnectionPrivate)); + + base_class->create_handle_repos = create_handle_repos; + base_class->get_unique_connection_name = get_unique_connection_name; + base_class->create_channel_factories = create_channel_factories; + base_class->start_connecting = start_connecting; + base_class->shut_down = shut_down; + + base_class->interfaces_always_present = interfaces_always_present; + + param_spec = g_param_spec_string ("account", "Account name", + "The username of this user", NULL, + 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 +tp_tests_simple_connection_set_identifier (TpTestsSimpleConnection *self, + const gchar *identifier) +{ + TpBaseConnection *conn = (TpBaseConnection *) self; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, + TP_HANDLE_TYPE_CONTACT); + TpHandle handle = tp_handle_ensure (contact_repo, identifier, NULL, NULL); + + /* if this fails then the identifier was bad - caller error */ + g_return_if_fail (handle != 0); + + tp_base_connection_set_self_handle (conn, handle); + tp_handle_unref (contact_repo, handle); +} + +TpTestsSimpleConnection * +tp_tests_simple_connection_new (const gchar *account, + const gchar *protocol) +{ + return TP_TESTS_SIMPLE_CONNECTION (g_object_new ( + TP_TESTS_TYPE_SIMPLE_CONNECTION, + "account", account, + "protocol", protocol, + NULL)); +} + +gchar * +tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self, + const gchar *target_id, + GHashTable **props) +{ + TpTestsTextChannelNull *chan; + gchar *chan_path; + TpHandleRepoIface *contact_repo; + TpHandle handle; + static guint count = 0; + TpBaseConnection *base_conn = (TpBaseConnection *) self; + + /* Get contact handle */ + contact_repo = tp_base_connection_get_handles (base_conn, + TP_HANDLE_TYPE_CONTACT); + g_assert (contact_repo != NULL); + + handle = tp_handle_ensure (contact_repo, target_id, NULL, NULL); + + chan = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle)); + if (chan != NULL) + { + /* Channel already exist, reuse it */ + g_object_get (chan, "object-path", &chan_path, NULL); + } + else + { + chan_path = g_strdup_printf ("%s/Channel%u", base_conn->object_path, + count++); + + chan = TP_TESTS_TEXT_CHANNEL_NULL ( + tp_tests_object_new_static_class ( + TP_TESTS_TYPE_TEXT_CHANNEL_NULL, + "connection", self, + "object-path", chan_path, + "handle", handle, + NULL)); + + g_hash_table_insert (self->priv->channels, GUINT_TO_POINTER (handle), + chan); + } + + tp_handle_unref (contact_repo, handle); + + if (props != NULL) + *props = tp_tests_text_channel_get_props (chan); + + 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 new file mode 100644 index 0000000..6322f4b --- /dev/null +++ b/tests/lib/simple-conn.h @@ -0,0 +1,77 @@ +/* + * simple-conn.h - header for a simple connection + * + * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef __TP_TESTS_SIMPLE_CONN_H__ +#define __TP_TESTS_SIMPLE_CONN_H__ + +#include <glib-object.h> +#include <telepathy-glib/base-connection.h> + +G_BEGIN_DECLS + +typedef struct _TpTestsSimpleConnection TpTestsSimpleConnection; +typedef struct _TpTestsSimpleConnectionClass TpTestsSimpleConnectionClass; +typedef struct _TpTestsSimpleConnectionPrivate TpTestsSimpleConnectionPrivate; + +struct _TpTestsSimpleConnectionClass { + TpBaseConnectionClass parent_class; +}; + +struct _TpTestsSimpleConnection { + TpBaseConnection parent; + + TpTestsSimpleConnectionPrivate *priv; +}; + +GType tp_tests_simple_connection_get_type (void); + +/* TYPE MACROS */ +#define TP_TESTS_TYPE_SIMPLE_CONNECTION \ + (tp_tests_simple_connection_get_type ()) +#define TP_TESTS_SIMPLE_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ + TpTestsSimpleConnection)) +#define TP_TESTS_SIMPLE_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ + TpTestsSimpleConnectionClass)) +#define TP_TESTS_SIMPLE_IS_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION)) +#define TP_TESTS_SIMPLE_IS_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CONNECTION)) +#define TP_TESTS_SIMPLE_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ + TpTestsSimpleConnectionClass)) + +TpTestsSimpleConnection * tp_tests_simple_connection_new (const gchar *account, + const gchar *protocol); + +/* Cause "network events", for debugging/testing */ + +void tp_tests_simple_connection_inject_disconnect ( + TpTestsSimpleConnection *self); + +void tp_tests_simple_connection_set_identifier (TpTestsSimpleConnection *self, + const gchar *identifier); + +gchar * tp_tests_simple_connection_ensure_text_chan ( + TpTestsSimpleConnection *self, + 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__ */ diff --git a/tests/lib/textchan-null.c b/tests/lib/textchan-null.c new file mode 100644 index 0000000..c41fceb --- /dev/null +++ b/tests/lib/textchan-null.c @@ -0,0 +1,570 @@ +/* + * /dev/null as a text channel + * + * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#include "textchan-null.h" + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/channel-iface.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/dbus-properties-mixin.h> +#include <telepathy-glib/interfaces.h> +#include <telepathy-glib/svc-channel.h> +#include <telepathy-glib/svc-generic.h> + +static void text_iface_init (gpointer iface, gpointer data); +static void channel_iface_init (gpointer iface, gpointer data); + +G_DEFINE_TYPE_WITH_CODE (TpTestsTextChannelNull, + tp_tests_text_channel_null, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)) + +G_DEFINE_TYPE_WITH_CODE (TpTestsPropsTextChannel, + tp_tests_props_text_channel, + TP_TESTS_TYPE_TEXT_CHANNEL_NULL, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init)) + +G_DEFINE_TYPE_WITH_CODE (TpTestsPropsGroupTextChannel, + tp_tests_props_group_text_channel, + TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, + tp_group_mixin_iface_init)) + +static const char *tp_tests_text_channel_null_interfaces[] = { NULL }; + +/* type definition stuff */ + +enum +{ + PROP_OBJECT_PATH = 1, + PROP_CHANNEL_TYPE, + PROP_HANDLE_TYPE, + PROP_HANDLE, + PROP_TARGET_ID, + PROP_CONNECTION, + PROP_INTERFACES, + PROP_REQUESTED, + PROP_INITIATOR_HANDLE, + PROP_INITIATOR_ID, + N_PROPS +}; + +struct _TpTestsTextChannelNullPrivate +{ + TpBaseConnection *conn; + gchar *object_path; + TpHandle handle; + + unsigned closed:1; + unsigned disposed:1; +}; + +static void +tp_tests_text_channel_null_init (TpTestsTextChannelNull *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TP_TESTS_TYPE_TEXT_CHANNEL_NULL, TpTestsTextChannelNullPrivate); +} + +static void +tp_tests_props_text_channel_init (TpTestsPropsTextChannel *self) +{ + self->dbus_property_interfaces_retrieved = g_hash_table_new (NULL, NULL); +} + +static GObject * +constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *object = + G_OBJECT_CLASS (tp_tests_text_channel_null_parent_class)->constructor (type, + n_props, props); + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + + tp_handle_ref (contact_repo, self->priv->handle); + + tp_dbus_daemon_register_object ( + tp_base_connection_get_dbus_daemon (self->priv->conn), + self->priv->object_path, self); + + tp_text_mixin_init (object, G_STRUCT_OFFSET (TpTestsTextChannelNull, text), + contact_repo); + + tp_text_mixin_set_message_types (object, + TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, + TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, + TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, + G_MAXUINT); + + return object; +} + +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, self->priv->object_path); + break; + case PROP_CHANNEL_TYPE: + g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT); + break; + case PROP_HANDLE_TYPE: + g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); + break; + case PROP_HANDLE: + g_value_set_uint (value, self->priv->handle); + break; + case PROP_TARGET_ID: + { + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( + self->priv->conn, TP_HANDLE_TYPE_CONTACT); + + g_value_set_string (value, + tp_handle_inspect (contact_repo, self->priv->handle)); + } + break; + case PROP_REQUESTED: + g_value_set_boolean (value, TRUE); + break; + case PROP_INITIATOR_HANDLE: + g_value_set_uint (value, self->priv->conn->self_handle); + break; + case PROP_INITIATOR_ID: + { + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( + self->priv->conn, TP_HANDLE_TYPE_CONTACT); + + g_value_set_string (value, + tp_handle_inspect (contact_repo, self->priv->conn->self_handle)); + } + break; + case PROP_INTERFACES: + g_value_set_boxed (value, tp_tests_text_channel_null_interfaces); + break; + case PROP_CONNECTION: + g_value_set_object (value, self->priv->conn); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); + + switch (property_id) + { + case PROP_OBJECT_PATH: + g_free (self->priv->object_path); + self->priv->object_path = g_value_dup_string (value); + break; + case PROP_HANDLE: + /* we don't ref it here because we don't necessarily have access to the + * contact repo yet - instead we ref it in the constructor. + */ + self->priv->handle = g_value_get_uint (value); + break; + case PROP_HANDLE_TYPE: + case PROP_CHANNEL_TYPE: + /* these properties are writable in the interface, but not actually + * meaningfully changable on this channel, so we do nothing */ + break; + case PROP_CONNECTION: + self->priv->conn = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +tp_tests_text_channel_null_close (TpTestsTextChannelNull *self) +{ + if (!self->priv->closed) + { + self->priv->closed = TRUE; + tp_svc_channel_emit_closed (self); + tp_dbus_daemon_unregister_object ( + tp_base_connection_get_dbus_daemon (self->priv->conn), self); + } +} + +static void +dispose (GObject *object) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); + + if (self->priv->disposed) + return; + + self->priv->disposed = TRUE; + tp_tests_text_channel_null_close (self); + + ((GObjectClass *) tp_tests_text_channel_null_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); + TpHandleRepoIface *contact_handles = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + + tp_handle_unref (contact_handles, self->priv->handle); + g_free (self->priv->object_path); + + tp_text_mixin_finalize (object); + + ((GObjectClass *) tp_tests_text_channel_null_parent_class)->finalize (object); +} + +static void +tp_tests_text_channel_null_class_init (TpTestsTextChannelNullClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (TpTestsTextChannelNullPrivate)); + + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + g_object_class_override_property (object_class, PROP_OBJECT_PATH, + "object-path"); + g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, + "channel-type"); + g_object_class_override_property (object_class, PROP_HANDLE_TYPE, + "handle-type"); + g_object_class_override_property (object_class, PROP_HANDLE, "handle"); + + param_spec = g_param_spec_object ("connection", "TpBaseConnection object", + "Connection object that owns this channel", + TP_TYPE_BASE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); + g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); + + param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", + "Additional Channel.Interface.* interfaces", + G_TYPE_STRV, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INTERFACES, param_spec); + + param_spec = g_param_spec_string ("target-id", "Peer's ID", + "The string obtained by inspecting the target handle", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec); + + param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle", + "The contact who initiated the channel", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE, + param_spec); + + param_spec = g_param_spec_string ("initiator-id", "Initiator's ID", + "The string obtained by inspecting the initiator-handle", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_INITIATOR_ID, + param_spec); + + param_spec = g_param_spec_boolean ("requested", "Requested?", + "True if this channel was requested by the local user", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_REQUESTED, param_spec); + + tp_text_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsTextChannelNullClass, text_class)); +} + +static void +tp_tests_props_text_channel_getter_gobject_properties (GObject *object, + GQuark interface, + GQuark name, + GValue *value, + gpointer getter_data) +{ + TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); + + g_hash_table_insert (self->dbus_property_interfaces_retrieved, + GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface)); + + tp_dbus_properties_mixin_getter_gobject_properties (object, interface, name, + value, getter_data); +} + +static void +props_finalize (GObject *object) +{ + TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); + + g_hash_table_unref (self->dbus_property_interfaces_retrieved); + + ((GObjectClass *) tp_tests_props_text_channel_parent_class)->finalize (object); +} + +static void +tp_tests_props_text_channel_class_init (TpTestsPropsTextChannelClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + static TpDBusPropertiesMixinPropImpl channel_props[] = { + { "TargetHandleType", "handle-type", NULL }, + { "TargetHandle", "handle", NULL }, + { "ChannelType", "channel-type", NULL }, + { "Interfaces", "interfaces", NULL }, + { "TargetID", "target-id", NULL }, + { "Requested", "requested", NULL }, + { "InitiatorHandle", "initiator-handle", NULL }, + { "InitiatorID", "initiator-id", NULL }, + { NULL } + }; + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { + { TP_IFACE_CHANNEL, + tp_tests_props_text_channel_getter_gobject_properties, + NULL, + channel_props, + }, + { NULL } + }; + + object_class->finalize = props_finalize; + + klass->dbus_properties_class.interfaces = prop_interfaces; + tp_dbus_properties_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsPropsTextChannelClass, dbus_properties_class)); +} + +static void +tp_tests_props_group_text_channel_init (TpTestsPropsGroupTextChannel *self) +{ +} + +static void +group_constructed (GObject *self) +{ + TpBaseConnection *conn = TP_TESTS_TEXT_CHANNEL_NULL (self)->priv->conn; + void (*chain_up) (GObject *) = + ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (self); + + tp_group_mixin_init (self, + G_STRUCT_OFFSET (TpTestsPropsGroupTextChannel, group), + tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT), + tp_base_connection_get_self_handle (conn)); + tp_group_mixin_change_flags (self, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); +} + +static void +group_finalize (GObject *self) +{ + tp_group_mixin_finalize (self); + + ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->finalize (self); +} + +static gboolean +dummy_add_remove_member (GObject *obj, + TpHandle handle, + const gchar *message, + GError **error) +{ + return TRUE; +} + +static void +group_iface_props_getter (GObject *object, + GQuark interface, + GQuark name, + GValue *value, + gpointer getter_data) +{ + TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); + + g_hash_table_insert (self->dbus_property_interfaces_retrieved, + GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface)); + + tp_group_mixin_get_dbus_property (object, interface, name, value, getter_data); +} + +static void +tp_tests_props_group_text_channel_class_init (TpTestsPropsGroupTextChannelClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + static TpDBusPropertiesMixinPropImpl group_props[] = { + { "GroupFlags", NULL, NULL }, + { "HandleOwners", NULL, NULL }, + { "LocalPendingMembers", NULL, NULL }, + { "Members", NULL, NULL }, + { "RemotePendingMembers", NULL, NULL }, + { "SelfHandle", NULL, NULL }, + { NULL } + }; + + object_class->constructed = group_constructed; + object_class->finalize = group_finalize; + + tp_group_mixin_class_init (object_class, + G_STRUCT_OFFSET (TpTestsPropsGroupTextChannelClass, group_class), + dummy_add_remove_member, + dummy_add_remove_member); + tp_dbus_properties_mixin_implement_interface (object_class, + TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP, group_iface_props_getter, NULL, + group_props); +} + +static void +channel_close (TpSvcChannel *iface, + DBusGMethodInvocation *context) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); + + tp_tests_text_channel_null_close (self); + tp_svc_channel_return_from_close (context); +} + +static void +channel_get_channel_type (TpSvcChannel *iface, + DBusGMethodInvocation *context) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); + + self->get_channel_type_called++; + + tp_svc_channel_return_from_get_channel_type (context, + TP_IFACE_CHANNEL_TYPE_TEXT); +} + +static void +channel_get_handle (TpSvcChannel *iface, + DBusGMethodInvocation *context) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); + + self->get_handle_called++; + + tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT, + self->priv->handle); +} + +static void +channel_get_interfaces (TpSvcChannel *iface, + DBusGMethodInvocation *context) +{ + TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); + + self->get_interfaces_called++; + + tp_svc_channel_return_from_get_interfaces (context, + tp_tests_text_channel_null_interfaces); +} + +static void +channel_iface_init (gpointer iface, + gpointer data) +{ + TpSvcChannelClass *klass = iface; + +#define IMPLEMENT(x) tp_svc_channel_implement_##x (klass, channel_##x) + IMPLEMENT (close); + IMPLEMENT (get_channel_type); + IMPLEMENT (get_handle); + IMPLEMENT (get_interfaces); +#undef IMPLEMENT +} + +static void +text_send (TpSvcChannelTypeText *iface, + guint type, + const gchar *text, + DBusGMethodInvocation *context) +{ + /* silently swallow the message */ + tp_svc_channel_type_text_return_from_send (context); +} + +static void +text_iface_init (gpointer iface, + gpointer data) +{ + TpSvcChannelTypeTextClass *klass = iface; + + tp_text_mixin_iface_init (iface, data); +#define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (klass, text_##x) + IMPLEMENT (send); +#undef IMPLEMENT +} + +GHashTable * +tp_tests_text_channel_get_props (TpTestsTextChannelNull *self) +{ + GHashTable *props; + TpHandleType handle_type; + TpHandle handle; + gchar *target_id; + gboolean requested; + TpHandle initiator_handle; + gchar *initiator_id; + GStrv interfaces; + + g_object_get (self, + "handle-type", &handle_type, + "handle", &handle, + "target-id", &target_id, + "requested", &requested, + "initiator-handle", &initiator_handle, + "initiator-id", &initiator_id, + "interfaces", &interfaces, + NULL); + + props = tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, handle_type, + TP_PROP_CHANNEL_TARGET_HANDLE, G_TYPE_UINT, handle, + TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, target_id, + TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, requested, + TP_PROP_CHANNEL_INITIATOR_HANDLE, G_TYPE_UINT, initiator_handle, + TP_PROP_CHANNEL_INITIATOR_ID, G_TYPE_STRING, initiator_id, + TP_PROP_CHANNEL_INTERFACES, G_TYPE_STRV, interfaces, + NULL); + + g_free (target_id); + g_free (initiator_id); + g_strfreev (interfaces); + return props; +} diff --git a/tests/lib/textchan-null.h b/tests/lib/textchan-null.h new file mode 100644 index 0000000..583bec5 --- /dev/null +++ b/tests/lib/textchan-null.h @@ -0,0 +1,137 @@ +/* + * /dev/null as a text channel + * + * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef __TP_TESTS_TEXT_CHANNEL_NULL_H__ +#define __TP_TESTS_TEXT_CHANNEL_NULL_H__ + +#include <glib-object.h> +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/text-mixin.h> +#include <telepathy-glib/group-mixin.h> + +G_BEGIN_DECLS + +typedef struct _TpTestsTextChannelNull TpTestsTextChannelNull; +typedef struct _TpTestsTextChannelNullClass TpTestsTextChannelNullClass; +typedef struct _TpTestsTextChannelNullPrivate TpTestsTextChannelNullPrivate; + +GType tp_tests_text_channel_null_get_type (void); + +#define TP_TESTS_TYPE_TEXT_CHANNEL_NULL \ + (tp_tests_text_channel_null_get_type ()) +#define TP_TESTS_TEXT_CHANNEL_NULL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ + TpTestsTextChannelNull)) +#define TP_TESTS_TEXT_CHANNEL_NULL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ + TpTestsTextChannelNullClass)) +#define TP_TESTS_IS_TEXT_CHANNEL_NULL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL)) +#define TP_TESTS_IS_TEXT_CHANNEL_NULL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_NULL)) +#define TP_TESTS_TEXT_CHANNEL_NULL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ + TpTestsTextChannelNullClass)) + +struct _TpTestsTextChannelNullClass { + GObjectClass parent_class; + + TpTextMixinClass text_class; +}; + +struct _TpTestsTextChannelNull { + GObject parent; + TpTextMixin text; + + guint get_handle_called; + guint get_interfaces_called; + guint get_channel_type_called; + + TpTestsTextChannelNullPrivate *priv; +}; + +/* Subclass with D-Bus properties */ + +typedef struct _TestPropsTextChannel TpTestsPropsTextChannel; +typedef struct _TestPropsTextChannelClass TpTestsPropsTextChannelClass; + +struct _TestPropsTextChannel { + TpTestsTextChannelNull parent; + + GHashTable *dbus_property_interfaces_retrieved; +}; + +struct _TestPropsTextChannelClass { + TpTestsTextChannelNullClass parent; + + TpDBusPropertiesMixinClass dbus_properties_class; +}; + +GType tp_tests_props_text_channel_get_type (void); + +#define TP_TESTS_TYPE_PROPS_TEXT_CHANNEL \ + (tp_tests_props_text_channel_get_type ()) +#define TP_TESTS_PROPS_TEXT_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ + TpTestsPropsTextChannel)) +#define TP_TESTS_PROPS_TEXT_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ + TpTestsPropsTextChannelClass)) +#define TP_TESTS_IS_PROPS_TEXT_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL)) +#define TP_TESTS_IS_PROPS_TEXT_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL)) +#define TP_TESTS_PROPS_TEXT_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ + TpTestsPropsTextChannelClass)) + +/* Subclass with D-Bus properties and Group */ + +typedef struct _TestPropsGroupTextChannel TpTestsPropsGroupTextChannel; +typedef struct _TestPropsGroupTextChannelClass TpTestsPropsGroupTextChannelClass; + +struct _TestPropsGroupTextChannel { + TpTestsPropsTextChannel parent; + + TpGroupMixin group; +}; + +struct _TestPropsGroupTextChannelClass { + TpTestsPropsTextChannelClass parent; + + TpGroupMixinClass group_class; +}; + +GType tp_tests_props_group_text_channel_get_type (void); + +#define TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL \ + (tp_tests_props_group_text_channel_get_type ()) +#define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ + TpTestsPropsGroupTextChannel)) +#define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ + TpTestsPropsGroupTextChannelClass)) +#define TP_TESTS_IS_PROPS_GROUP_TEXT_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL)) +#define TP_TESTS_IS_PROPS_GROUP_TEXT_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL)) +#define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ + TpTestsPropsGroupTextChannelClass)) + +void tp_tests_text_channel_null_close (TpTestsTextChannelNull *self); + +GHashTable * tp_tests_text_channel_get_props (TpTestsTextChannelNull *self); + +G_END_DECLS + +#endif /* #ifndef __TP_TESTS_TEXT_CHANNEL_NULL_H__ */ diff --git a/tests/lib/util.c b/tests/lib/util.c new file mode 100644 index 0000000..f5f3d66 --- /dev/null +++ b/tests/lib/util.c @@ -0,0 +1,291 @@ +/* Simple utility code used by the regression tests. + * + * Copyright © 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#include "util.h" + +#ifdef G_OS_UNIX +# include <unistd.h> /* for alarm() */ +#endif + +void +tp_tests_proxy_run_until_prepared (gpointer proxy, + const GQuark *features) +{ + GError *error = NULL; + + tp_tests_proxy_run_until_prepared_or_failed (proxy, features, &error); + g_assert_no_error (error); +} + +/* A GAsyncReadyCallback whose user_data is a GAsyncResult **. It writes a + * reference to the result into that pointer. */ +void +tp_tests_result_ready_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + GAsyncResult **result = user_data; + + *result = g_object_ref (res); +} + +/* Run until *result contains a result. Intended to be used with a pending + * async call that uses tp_tests_result_ready_cb. */ +void +tp_tests_run_until_result (GAsyncResult **result) +{ + /* not synchronous */ + g_assert (*result == NULL); + + while (*result == NULL) + g_main_context_iteration (NULL, TRUE); +} + +gboolean +tp_tests_proxy_run_until_prepared_or_failed (gpointer proxy, + const GQuark *features, + GError **error) +{ + GAsyncResult *result = NULL; + gboolean r; + + tp_proxy_prepare_async (proxy, features, tp_tests_result_ready_cb, &result); + + tp_tests_run_until_result (&result); + + r = tp_proxy_prepare_finish (proxy, result, error); + g_object_unref (result); + return r; +} + +TpDBusDaemon * +tp_tests_dbus_daemon_dup_or_die (void) +{ + TpDBusDaemon *d = tp_dbus_daemon_dup (NULL); + + /* In a shared library, this would be very bad (see fd.o #18832), but in a + * regression test that's going to be run under a temporary session bus, + * it's just what we want. */ + if (d == NULL) + { + g_error ("Unable to connect to session bus"); + } + + return d; +} + +static void +introspect_cb (TpProxy *proxy G_GNUC_UNUSED, + const gchar *xml G_GNUC_UNUSED, + const GError *error G_GNUC_UNUSED, + gpointer user_data, + GObject *weak_object G_GNUC_UNUSED) +{ + g_main_loop_quit (user_data); +} + +void +tp_tests_proxy_run_until_dbus_queue_processed (gpointer proxy) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + tp_cli_dbus_introspectable_call_introspect (proxy, -1, introspect_cb, + loop, NULL, NULL); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +typedef struct { + GMainLoop *loop; + TpHandle handle; +} HandleRequestResult; + +static void +handles_requested_cb (TpConnection *connection G_GNUC_UNUSED, + TpHandleType handle_type G_GNUC_UNUSED, + guint n_handles, + const TpHandle *handles, + const gchar * const *ids G_GNUC_UNUSED, + const GError *error, + gpointer user_data, + GObject *weak_object G_GNUC_UNUSED) +{ + HandleRequestResult *result = user_data; + + g_assert_no_error ((GError *) error); + g_assert_cmpuint (n_handles, ==, 1); + result->handle = handles[0]; +} + +static void +handle_request_result_finish (gpointer r) +{ + HandleRequestResult *result = r; + + g_main_loop_quit (result->loop); +} + +TpHandle +tp_tests_connection_run_request_contact_handle (TpConnection *connection, + const gchar *id) +{ + HandleRequestResult result = { g_main_loop_new (NULL, FALSE), 0 }; + const gchar * const ids[] = { id, NULL }; + + tp_connection_request_handles (connection, -1, TP_HANDLE_TYPE_CONTACT, ids, + handles_requested_cb, &result, handle_request_result_finish, NULL); + g_main_loop_run (result.loop); + g_main_loop_unref (result.loop); + return result.handle; +} + +void +_test_assert_empty_strv (const char *file, + int line, + gconstpointer strv) +{ + const gchar * const *strings = strv; + + if (strv != NULL && strings[0] != NULL) + { + guint i; + + g_message ("%s:%d: expected empty strv, but got:", file, line); + + for (i = 0; strings[i] != NULL; i++) + { + g_message ("* \"%s\"", strings[i]); + } + + g_error ("%s:%d: strv wasn't empty (see above for contents", + file, line); + } +} + +void +_tp_tests_assert_strv_equals (const char *file, + int line, + const char *expected_desc, + gconstpointer expected_strv, + const char *actual_desc, + gconstpointer actual_strv) +{ + const gchar * const *expected = expected_strv; + const gchar * const *actual = actual_strv; + guint i; + + g_assert (expected != NULL); + g_assert (actual != NULL); + + for (i = 0; expected[i] != NULL || actual[i] != NULL; i++) + { + if (expected[i] == NULL) + { + g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " + "NULL == %s", file, line, expected_desc, i, + actual_desc, i, actual[i]); + } + else if (actual[i] == NULL) + { + g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " + "%s == NULL", file, line, expected_desc, i, + actual_desc, i, expected[i]); + } + else if (tp_strdiff (expected[i], actual[i])) + { + g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " + "%s == %s", file, line, expected_desc, i, + actual_desc, i, expected[i], actual[i]); + } + } +} + +void +tp_tests_create_and_connect_conn (GType conn_type, + const gchar *account, + TpBaseConnection **service_conn, + TpConnection **client_conn) +{ + TpDBusDaemon *dbus; + gchar *name; + gchar *conn_path; + GError *error = NULL; + GQuark conn_features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; + + g_assert (service_conn != NULL); + g_assert (client_conn != NULL); + + dbus = tp_tests_dbus_daemon_dup_or_die (); + + *service_conn = tp_tests_object_new_static_class ( + conn_type, + "account", account, + "protocol", "simple", + NULL); + g_assert (*service_conn != NULL); + + g_assert (tp_base_connection_register (*service_conn, "simple", + &name, &conn_path, &error)); + g_assert_no_error (error); + + *client_conn = tp_connection_new (dbus, name, conn_path, + &error); + g_assert (*client_conn != NULL); + g_assert_no_error (error); + + tp_cli_connection_call_connect (*client_conn, -1, NULL, NULL, NULL, NULL); + tp_tests_proxy_run_until_prepared (*client_conn, conn_features); + + g_free (name); + g_free (conn_path); + + g_object_unref (dbus); +} + +/* This object exists solely so that tests/tests.supp can ignore "leaked" + * classes. */ +gpointer +tp_tests_object_new_static_class (GType type, + ...) +{ + va_list ap; + GObject *object; + const gchar *first_property; + + va_start (ap, type); + first_property = va_arg (ap, const gchar *); + object = g_object_new_valist (type, first_property, ap); + va_end (ap); + return object; +} + +static gboolean +time_out (gpointer nil G_GNUC_UNUSED) +{ + g_error ("Timed out"); + g_assert_not_reached (); + return FALSE; +} + +void +tp_tests_abort_after (guint sec) +{ + if (g_getenv ("TP_TESTS_NO_TIMEOUT") != NULL) + return; + + g_timeout_add_seconds (sec, time_out, NULL); + +#ifdef G_OS_UNIX + /* On Unix, we can kill the process more reliably; this is a safety-catch + * in case it deadlocks or something, in which case the main loop won't be + * processed. The default handler for SIGALRM is process termination. */ + alarm (sec + 2); +#endif +} diff --git a/tests/lib/util.h b/tests/lib/util.h new file mode 100644 index 0000000..7eab77d --- /dev/null +++ b/tests/lib/util.h @@ -0,0 +1,57 @@ +/* Simple utility code used by the regression tests. + * + * Copyright © 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2008 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef __TP_TESTS_LIB_UTIL_H__ +#define __TP_TESTS_LIB_UTIL_H__ + +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/base-connection.h> + +TpDBusDaemon *tp_tests_dbus_daemon_dup_or_die (void); + +void tp_tests_proxy_run_until_dbus_queue_processed (gpointer proxy); + +TpHandle tp_tests_connection_run_request_contact_handle ( + TpConnection *connection, + const gchar *id); + +void tp_tests_proxy_run_until_prepared (gpointer proxy, + const GQuark *features); +gboolean tp_tests_proxy_run_until_prepared_or_failed (gpointer proxy, + const GQuark *features, + GError **error); + +#define test_assert_empty_strv(strv) \ + _test_assert_empty_strv (__FILE__, __LINE__, strv) +void _test_assert_empty_strv (const char *file, int line, gconstpointer strv); + +#define tp_tests_assert_strv_equals(actual, expected) \ + _tp_tests_assert_strv_equals (__FILE__, __LINE__, \ + #actual, actual, \ + #expected, expected) +void _tp_tests_assert_strv_equals (const char *file, int line, + const char *actual_desc, gconstpointer actual_strv, + const char *expected_desc, gconstpointer expected_strv); + +void tp_tests_create_and_connect_conn (GType conn_type, + const gchar *account, + TpBaseConnection **service_conn, + TpConnection **client_conn); + +gpointer tp_tests_object_new_static_class (GType type, + ...) G_GNUC_NULL_TERMINATED; + +void tp_tests_run_until_result (GAsyncResult **result); +void tp_tests_result_ready_cb (GObject *object, + GAsyncResult *res, gpointer user_data); + +void tp_tests_abort_after (guint sec); + +#endif /* #ifndef __TP_TESTS_LIB_UTIL_H__ */ diff --git a/tests/suppressions/tpl.supp b/tests/suppressions/tpl.supp index c92f72a..15925d8 100644 --- a/tests/suppressions/tpl.supp +++ b/tests/suppressions/tpl.supp @@ -4,13 +4,13 @@ # ============================= selinux ================================== { - selinux getdelim - Memcheck:Leak - fun:malloc - fun:getdelim - obj:/lib/libselinux.so.1 - obj:/lib/libselinux.so.1 - obj:/lib/libselinux.so.1 + selinux getdelim + Memcheck:Leak + fun:malloc + fun:getdelim + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 + obj:/lib/libselinux.so.1 } @@ -147,10 +147,17 @@ } { - g_type_create_instance - Memcheck:Leak - ... - fun:g_type_create_instance + g_type_create_instance + Memcheck:Leak + ... + fun:g_type_create_instance +} + +{ + g_type_add_interface_static + Memcheck:Leak + ... + fun:g_type_add_interface_static } # ============================= dbus-glib ============================= @@ -242,6 +249,23 @@ fun:tp_dbus_daemon_constructor } +{ + Leak in tp-glib 0.11.16 (Fedora 14) + Memcheck:Leak + ... + fun:g_simple_async_result_new + fun:tp_proxy_prepare_async +} + +# ============================= tp-logger-tests ========================== + +{ + tp_tests_object_new_static_class + Memcheck:Leak + ... + fun:tp_tests_object_new_static_class +} + # ============================= unclassified ========================== { diff --git a/tests/test-entity.c b/tests/test-entity.c index 93cb5c8..5877a5d 100644 --- a/tests/test-entity.c +++ b/tests/test-entity.c @@ -3,6 +3,9 @@ #include <telepathy-logger/entity.h> #include <telepathy-logger/entity-internal.h> +#include "lib/util.h" +#include "lib/contacts-conn.h" + static void test_entity_instantiation (void) { @@ -38,12 +41,109 @@ test_entity_instantiation_from_room_id (void) g_object_unref (entity); } + +typedef struct { + TpContact *contacts[2]; + GMainLoop *loop; +} Result; + + +static void +get_contacts_cb (TpConnection *connection, + guint n_contacts, + TpContact * const *contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + Result *result = user_data; + + g_assert_no_error (error); + g_assert (n_contacts == 2); + g_assert (n_failed == 0); + + result->contacts[0] = g_object_ref (contacts[0]); + result->contacts[1] = g_object_ref (contacts[1]); + + g_main_loop_quit (result->loop); +} + + static void test_entity_instantiation_from_tp_contact (void) { - /* TODO figure-out how to obtain a TpContact to test - * _tpl_entity_new_from_tp_contact() */ - g_printf ("- TODO - "); + TpBaseConnection *base_connection; + TpConnection *client_connection; + TpTestsContactsConnection *connection; + TpHandleRepoIface *repo; + TpHandle handles[2]; + const char *alias[] = {"Alice in Wonderland", "Bob the builder"}; + const char *avatar_tokens[] = {"alice-token", NULL}; + TpContactFeature features[] = + { TP_CONTACT_FEATURE_ALIAS, TP_CONTACT_FEATURE_AVATAR_TOKEN }; + Result result; + TplEntity *entity; + + tp_tests_create_and_connect_conn (TP_TESTS_TYPE_CONTACTS_CONNECTION, + "me@test.com", &base_connection, &client_connection); + + connection = TP_TESTS_CONTACTS_CONNECTION (base_connection); + + repo = tp_base_connection_get_handles (base_connection, + TP_HANDLE_TYPE_CONTACT); + + handles[0] = tp_handle_ensure (repo, "alice", NULL, NULL); + g_assert (handles[0] != 0); + + handles[1] = tp_handle_ensure (repo, "bob", NULL, NULL); + g_assert (handles[1] != 0); + + tp_tests_contacts_connection_change_aliases (connection, 2, handles, + alias); + tp_tests_contacts_connection_change_avatar_tokens (connection, 2, handles, + avatar_tokens); + + result.contacts[0] = result.contacts[1] = 0; + result.loop = g_main_loop_new (NULL, FALSE); + + tp_connection_get_contacts_by_handle (client_connection, + 2, handles, + 2, features, + get_contacts_cb, &result, + NULL, NULL); + g_main_loop_run (result.loop); + + entity = _tpl_entity_new_from_tp_contact (result.contacts[0], + TPL_ENTITY_SELF); + + g_assert_cmpstr (tpl_entity_get_identifier (entity), ==, "alice"); + g_assert (tpl_entity_get_entity_type (entity) == TPL_ENTITY_SELF); + g_assert_cmpstr (tpl_entity_get_alias (entity), ==, alias[0]); + g_assert_cmpstr (tpl_entity_get_avatar_token (entity), ==, avatar_tokens[0]); + g_object_unref (entity); + + entity = _tpl_entity_new_from_tp_contact (result.contacts[1], + TPL_ENTITY_CONTACT); + + g_assert_cmpstr (tpl_entity_get_identifier (entity), ==, "bob"); + g_assert (tpl_entity_get_entity_type (entity) == TPL_ENTITY_CONTACT); + g_assert_cmpstr (tpl_entity_get_alias (entity), ==, alias[1]); + g_assert (tpl_entity_get_avatar_token (entity) == NULL); + g_object_unref (entity); + + g_object_unref (result.contacts[0]); + g_object_unref (result.contacts[1]); + g_main_loop_unref (result.loop); + + tp_base_connection_change_status (base_connection, + TP_CONNECTION_STATUS_DISCONNECTED, + TP_CONNECTION_STATUS_REASON_REQUESTED); + tp_base_connection_finish_shutdown (base_connection); + + g_object_unref (base_connection); + g_object_unref (client_connection); } int main (int argc, |