diff options
60 files changed, 2207 insertions, 468 deletions
diff --git a/configure.ac b/configure.ac index 7340cb34..568fd9bb 100644 --- a/configure.ac +++ b/configure.ac @@ -183,7 +183,7 @@ AM_CONDITIONAL([OUT_OF_TREE_BUILD], [test "z$ac_srcdir" != z.]) dnl Check for Glib PKG_CHECK_MODULES(GLIB, - [glib-2.0 >= 2.25.16, gobject-2.0 >= 2.25.16, gio-2.0 >= 2.25.16]) + [glib-2.0 >= 2.28.0, gobject-2.0 >= 2.28.0, gio-2.0 >= 2.28.0]) dnl Check for GIO-Unix PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0], diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index d95ce7fb..723d1d59 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -468,6 +468,9 @@ TP_SVC_CHANNEL_INTERFACE_PASSWORD_GET_CLASS TpSvcChannelInterfaceSMS TpSvcChannelInterfaceSMSClass tp_svc_channel_interface_sms_emit_sms_channel_changed +tp_svc_channel_interface_sms_get_sms_length_impl +tp_svc_channel_interface_sms_implement_get_sms_length +tp_svc_channel_interface_sms_return_from_get_sms_length <SUBSECTION Standard> tp_svc_channel_interface_sms_get_type TP_TYPE_SVC_CHANNEL_INTERFACE_SMS @@ -2937,6 +2940,7 @@ tp_iface_quark_connection_interface_service_point <TITLE>proxy</TITLE> <INCLUDE>telepathy-glib/proxy.h</INCLUDE> TpProxy +TpProxyPrepareAsync TpProxyFeature TpProxyClassFeatureListFunc TpProxyClass @@ -2970,6 +2974,7 @@ TP_IS_PROXY_CLASS TP_PROXY TP_PROXY_CLASS TP_PROXY_GET_CLASS +TpProxyFeaturePrivate </SECTION> <SECTION> @@ -3225,6 +3230,9 @@ tp_cli_channel_interface_password_signal_callback_password_flags_changed <SUBSECTION> tp_cli_channel_interface_sms_connect_to_sms_channel_changed tp_cli_channel_interface_sms_signal_callback_sms_channel_changed +tp_cli_channel_interface_sms_call_get_sms_length +tp_cli_channel_interface_sms_callback_for_get_sms_length +tp_cli_channel_interface_sms_run_get_sms_length </SECTION> <SECTION> @@ -4440,6 +4448,10 @@ tp_cli_channel_dispatcher_callback_for_ensure_channel tp_cli_channel_dispatcher_call_ensure_channel tp_cli_channel_dispatcher_callback_for_ensure_channel_with_hints tp_cli_channel_dispatcher_call_ensure_channel_with_hints +tp_cli_channel_dispatcher_call_delegate_channels +tp_cli_channel_dispatcher_call_present_channel +tp_cli_channel_dispatcher_callback_for_delegate_channels +tp_cli_channel_dispatcher_callback_for_present_channel <SUBSECTION> tp_cli_channel_dispatcher_interface_operation_list_signal_callback_dispatch_operation_finished tp_cli_channel_dispatcher_interface_operation_list_connect_to_dispatch_operation_finished @@ -4475,6 +4487,12 @@ tp_svc_channel_dispatcher_implement_ensure_channel tp_svc_channel_dispatcher_return_from_ensure_channel_with_hints tp_svc_channel_dispatcher_ensure_channel_with_hints_impl tp_svc_channel_dispatcher_implement_ensure_channel_with_hints +tp_svc_channel_dispatcher_delegate_channels_impl +tp_svc_channel_dispatcher_implement_delegate_channels +tp_svc_channel_dispatcher_implement_present_channel +tp_svc_channel_dispatcher_present_channel_impl +tp_svc_channel_dispatcher_return_from_delegate_channels +tp_svc_channel_dispatcher_return_from_present_channel <SUBSECTION> TpSvcChannelDispatcherInterfaceOperationList TpSvcChannelDispatcherInterfaceOperationListClass @@ -4524,6 +4542,8 @@ tp_channel_dispatch_operation_handle_with_time_async tp_channel_dispatch_operation_handle_with_time_finish tp_channel_dispatch_operation_claim_async tp_channel_dispatch_operation_claim_finish +tp_channel_dispatch_operation_claim_with_async +tp_channel_dispatch_operation_claim_with_finish <SUBSECTION Standard> TP_CHANNEL_DISPATCH_OPERATION TP_CHANNEL_DISPATCH_OPERATION_CLASS diff --git a/examples/client/approver.c b/examples/client/approver.c index f100f52c..1a48fced 100644 --- a/examples/client/approver.c +++ b/examples/client/approver.c @@ -59,7 +59,7 @@ claim_cb (GObject *source, GPtrArray *channels; guint i; - if (!tp_channel_dispatch_operation_claim_finish (cdo, result, &error)) + if (!tp_channel_dispatch_operation_claim_with_finish (cdo, result, &error)) { g_print ("Claim() failed: %s\n", error->message); g_error_free (error); @@ -137,7 +137,8 @@ add_dispatch_operation_cb (TpSimpleApprover *self, { g_print ("Dissaprove channels\n"); - tp_channel_dispatch_operation_claim_async (cdo, claim_cb, NULL); + tp_channel_dispatch_operation_claim_with_async (cdo, + TP_BASE_CLIENT (self), claim_cb, NULL); } else { diff --git a/m4/tp-linker-flag.m4 b/m4/tp-linker-flag.m4 index 23eb41ad..8fd35068 100644 --- a/m4/tp-linker-flag.m4 +++ b/m4/tp-linker-flag.m4 @@ -22,7 +22,7 @@ AC_DEFUN([TP_LINKER_FLAG], save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $1" - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], []), [flag_ok=yes], [flag_ok=no]) + AC_COMPILE_IFELSE(AC_LANG_SOURCE([]), [flag_ok=yes], [flag_ok=no]) LDFLAGS="$save_LDFLAGS" diff --git a/spec/Channel_Dispatcher.xml b/spec/Channel_Dispatcher.xml index 2dd000b4..2de52800 100644 --- a/spec/Channel_Dispatcher.xml +++ b/spec/Channel_Dispatcher.xml @@ -168,10 +168,10 @@ <tp:possible-errors> <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> <tp:docstring> - The Preferred_Handler is syntactically invalid or does + The <var>Preferred_Handler</var> is syntactically invalid or does not start with <code>org.freedesktop.Telepathy.Client.</code>, - the Account does not exist, or one of the Requested_Properties - is invalid + the <var>Account</var> does not exist, or one of the + <var>Requested_Properties</var> is invalid </tp:docstring> </tp:error> </tp:possible-errors> @@ -240,10 +240,10 @@ <tp:possible-errors> <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> <tp:docstring> - The Preferred_Handler is syntactically invalid or does + The <var>Preferred_Handler</var> is syntactically invalid or does not start with <code>org.freedesktop.Telepathy.Client.</code>, - the Account does not exist, or one of the Requested_Properties - is invalid + the <var>Account</var> does not exist, or one of the + <var>Requested_Properties</var> is invalid </tp:docstring> </tp:error> </tp:possible-errors> @@ -418,10 +418,10 @@ <tp:possible-errors> <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> <tp:docstring> - The Preferred_Handler is syntactically invalid or does + The <var>Preferred_Handler</var> is syntactically invalid or does not start with <code>org.freedesktop.Telepathy.Client.</code>, - the Account does not exist, or one of the Requested_Properties - is invalid + the <var>Account</var> does not exist, or one of the + <var>Requested_Properties</var> is invalid </tp:docstring> </tp:error> </tp:possible-errors> @@ -559,16 +559,139 @@ <tp:possible-errors> <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> <tp:docstring> - The Preferred_Handler is syntactically invalid or does + The <var>Preferred_Handler</var> is syntactically invalid or does not start with <code>org.freedesktop.Telepathy.Client.</code>, - the Account does not exist, or one of the Requested_Properties - is invalid + the <var>Account</var> does not exist, or one of the + <var>Requested_Properties</var> is invalid + </tp:docstring> + </tp:error> + </tp:possible-errors> + + </method> + + <method name="DelegateChannels" + tp:name-for-bindings="Delegate_Channels"> + <tp:added version="0.23.1" /> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Called by a + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client">Handler</tp:dbus-ref> + to redispatch a bunch of channels it is currently handling.</p> + + <p>If another + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client">Handler</tp:dbus-ref> + can be found, + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client.Handler">HandleChannels</tp:dbus-ref> + will be called on it and this function will succeed. In that case, + the original <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client">Handler</tp:dbus-ref> + does not longer handle those channels.</p> + + <p>If this method fails, the original + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client">Handler</tp:dbus-ref> + is still handling the channels.</p> + + </tp:docstring> + + <arg direction="in" name="Channels" type="ao"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The list of channels to redispatch. The caller has to be the current + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Client">Handler</tp:dbus-ref> + of all of these channels + </p> + </tp:docstring> + </arg> + + <arg direction="in" name="User_Action_Time" type="x" + tp:type="User_Action_Timestamp"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The time at which user action occurred, or 0 if this channels + delegation is for some reason not involving user action.</p> + + <p>This parameter is used in the same way as the corresponding + parameter to + <tp:member-ref>CreateChannelWithHints</tp:member-ref>.</p> + </tp:docstring> + </arg> + + <arg direction="in" name="Preferred_Handler" type="s" + tp:type="DBus_Well_Known_Name"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Either the well-known bus name (starting with + <code>org.freedesktop.Telepathy.Client.</code>) + of the preferred new handler for these + channels, or an empty string to indicate that any handler would be + acceptable. The behaviour and rationale are the same as for the + corresponding parameter to + <tp:member-ref>CreateChannelWithHints</tp:member-ref>.</p> + + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> + <tp:docstring> + The Preferred_Handler is syntactically invalid or does + not start with <code>org.freedesktop.Telepathy.Client.</code> or + the Account does not exist. + </tp:docstring> + </tp:error> + + <tp:error name="org.freedesktop.Telepathy.Error.NotYours"> + <tp:docstring> + The caller is not currently handling the channels. + </tp:docstring> + </tp:error> + + <tp:error name="org.freedesktop.Telepathy.Error.NotCapable"> + <tp:docstring> + There is no other suitable Handler. </tp:docstring> </tp:error> </tp:possible-errors> </method> + <method name="PresentChannel" + tp:name-for-bindings="Present_Channel"> + <tp:added version="0.23.1" /> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Equivalent of calling + <tp:dbus-ref namespace="org.freedesktop.Telepathy.ChannelDispatcher">EnsureChannel</tp:dbus-ref> + with a <var>Requested_Properties</var> which would result in ensuring + <var>Channel</var>.</p> + + <p>If <var>Channel</var> is handled, its handler will be asked to present it the user + (e.g. bring it into the foreground).</p> + </tp:docstring> + + <arg direction="in" name="Channel" type="o"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The channel to present.</p> + </tp:docstring> + </arg> + + <arg direction="in" name="User_Action_Time" type="x" + tp:type="User_Action_Timestamp"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The time at which user action occurred, or 0 if this channel + request is for some reason not involving user action.</p> + + <p>This parameter is used in the same way as the corresponding + parameter to + <tp:member-ref>EnsureChannelWithHints</tp:member-ref>.</p> + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> + <tp:docstring> + The Account does not exist, the Channel does not exist or it + does not belong to the Account. + </tp:docstring> + </tp:error> + + </tp:possible-errors> + </method> + <property name="SupportsRequestHints" tp:name-for-bindings="Supports_Request_Hints" type="b" access="read"> diff --git a/spec/Channel_Interface_SMS.xml b/spec/Channel_Interface_SMS.xml index 235046c3..480a2a77 100644 --- a/spec/Channel_Interface_SMS.xml +++ b/spec/Channel_Interface_SMS.xml @@ -205,5 +205,83 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <tp:member-ref>SMSChannel</tp:member-ref> property. </tp:docstring> </signal> + + <method name="GetSMSLength" + tp:name-for-bindings="Get_SMS_Length"> + <tp:added version="0.23.1"/> + + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Returns the number of 140 octet chunks required to send a message + via SMS, as well as the number of remaining characters available in + the final chunk and, if possible, an estimate of the cost.</p> + + <tp:rationale> + <p>There are a number of different SMS encoding mechanisms, and the + client doesn't know which mechanisms an individual CM might support. + This method allows the client, without any knowledge of the + encoding mechanism, to provide length details to the user.</p> + </tp:rationale> + + <p>Clients SHOULD limit the frequency with which this method is called + and SHOULD NOT call it for every keystroke. Clients MAY estimate the + remaining size between single keystrokes.</p> + </tp:docstring> + + <arg name="Message" type="aa{sv}" tp:type="Message_Part[]" direction="in"> + <tp:docstring> + The message the user wishes to send. + </tp:docstring> + </arg> + + <arg name="Chunks_Required" type="u" direction="out"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The number of 140 octet chunks required to send this message.</p> + + <p>For example, in the GSM standard 7-bit encoding, a 162 character + message would require 2 chunks.</p> + </tp:docstring> + </arg> + + <arg name="Remaining_Characters" type="i" direction="out"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The number of further characters that can be fit in the final + chunk. A negative value indicates that the message will be + truncated by <code>abs(Remaining_Characters)</code>. The value + <code>MIN_INT32</code> (<code>-2<sup>31</sup></code>) + indicates the message will be truncated by an unknown amount.</p> + + <p>For example, in the GSM standard 7-bit encoding, a 162 character + message would return 144 remaining characters (because of the + space required for the multipart SMS header).</p> + </tp:docstring> + </arg> + + <arg name="Estimated_Cost" type="i" direction="out"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The estimated cost of sending this message. The currency and + scale of this value are the same as the + <tp:dbus-ref namespace="ofdT.Connection.Interface">Balance.AccountBalance</tp:dbus-ref> + property.</p> + + <p>A value of <code>-1</code> indicates the cost could not be + estimated.</p> + + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"> + <tp:docstring> + Raised when the method is not available on this channel. + Clients MAY choose to make their own estimation. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> + <tp:docstring> + Raised when the content cannot be encoded into a valid SMS. + </tp:docstring> + </tp:error> + </tp:possible-errors> + </method> </interface> </node> diff --git a/spec/Connection_Interface_Location.xml b/spec/Connection_Interface_Location.xml index fe549234..c4fd68c3 100644 --- a/spec/Connection_Interface_Location.xml +++ b/spec/Connection_Interface_Location.xml @@ -60,7 +60,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</ <!-- Potentially to be reinstated later: http://bugs.freedesktop.org/show_bug.cgi?id=19585 - <tp:enum name="Location_Accuracy_Level" type="i"> + <tp:enum name="Location_Accuracy_Level" type="u"> <tp:docstring> A location accuracy level. This should be kept in sync with GeoclueAccuracyLevel in the Geoclue project. diff --git a/spec/all.xml b/spec/all.xml index d1493eb5..e23c6c6f 100644 --- a/spec/all.xml +++ b/spec/all.xml @@ -3,7 +3,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"> <tp:title>Telepathy D-Bus Interface Specification</tp:title> -<tp:version>0.22.2</tp:version> +<tp:version>0.23.1</tp:version> <tp:copyright>Copyright © 2005-2011 Collabora Limited</tp:copyright> <tp:copyright>Copyright © 2005-2011 Nokia Corporation</tp:copyright> diff --git a/telepathy-glib/account-manager.c b/telepathy-glib/account-manager.c index c0329143..1e3802f0 100644 --- a/telepathy-glib/account-manager.c +++ b/telepathy-glib/account-manager.c @@ -166,7 +166,7 @@ _tp_account_manager_list_features (TpProxyClass *cls G_GNUC_UNUSED) { features[FEAT_CORE].name = TP_ACCOUNT_MANAGER_FEATURE_CORE; features[FEAT_CORE].core = TRUE; - /* no need for a start_preparing function - the constructor starts it */ + /* no need for a prepare_async function - the constructor starts it */ /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c index e7e294e5..b097e1eb 100644 --- a/telepathy-glib/account.c +++ b/telepathy-glib/account.c @@ -172,8 +172,15 @@ enum { PROP_STORAGE_RESTRICTIONS }; -static void tp_account_maybe_prepare_addressing (TpProxy *proxy); -static void tp_account_maybe_prepare_storage (TpProxy *proxy); +static void tp_account_prepare_addressing_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data); + +static void tp_account_prepare_storage_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data); /** * TP_ACCOUNT_FEATURE_CORE: @@ -275,15 +282,15 @@ _tp_account_list_features (TpProxyClass *cls G_GNUC_UNUSED) { features[FEAT_CORE].name = TP_ACCOUNT_FEATURE_CORE; features[FEAT_CORE].core = TRUE; - /* no need for a start_preparing function - the constructor starts it */ + /* no need for a prepare_async function - the constructor starts it */ features[FEAT_ADDRESSING].name = TP_ACCOUNT_FEATURE_ADDRESSING; - features[FEAT_ADDRESSING].start_preparing = - tp_account_maybe_prepare_addressing; + features[FEAT_ADDRESSING].prepare_async = + tp_account_prepare_addressing_async; features[FEAT_STORAGE].name = TP_ACCOUNT_FEATURE_STORAGE; - features[FEAT_STORAGE].start_preparing = - tp_account_maybe_prepare_storage; + features[FEAT_STORAGE].prepare_async = + tp_account_prepare_storage_async; /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -413,6 +420,7 @@ _tp_account_got_all_storage_cb (TpProxy *proxy, GObject *object) { TpAccount *self = TP_ACCOUNT (proxy); + GSimpleAsyncResult *result = user_data; if (error != NULL) DEBUG ("Error getting Storage properties: %s", error->message); @@ -435,23 +443,26 @@ _tp_account_got_all_storage_cb (TpProxy *proxy, if (self->priv->storage_provider == NULL) self->priv->storage_provider = g_strdup (""); - _tp_proxy_set_feature_prepared (proxy, TP_ACCOUNT_FEATURE_STORAGE, TRUE); + g_simple_async_result_complete (result); } static void -tp_account_maybe_prepare_storage (TpProxy *proxy) +tp_account_prepare_storage_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpAccount *self = TP_ACCOUNT (proxy); + GSimpleAsyncResult *result; - if (self->priv->storage_provider != NULL) - return; + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_account_prepare_storage_async); - if (!_tp_proxy_is_preparing (proxy, TP_ACCOUNT_FEATURE_STORAGE)) - return; + g_assert (self->priv->storage_provider == NULL); tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_ACCOUNT_INTERFACE_STORAGE, - _tp_account_got_all_storage_cb, NULL, NULL, G_OBJECT (self)); + _tp_account_got_all_storage_cb, result, g_object_unref, G_OBJECT (self)); } static void @@ -783,9 +794,6 @@ _tp_account_update (TpAccount *account, } _tp_proxy_set_feature_prepared (proxy, TP_ACCOUNT_FEATURE_CORE, TRUE); - - tp_account_maybe_prepare_storage (proxy); - tp_account_maybe_prepare_addressing (proxy); } static void @@ -3721,6 +3729,7 @@ _tp_account_got_all_addressing_cb (TpProxy *proxy, GObject *object) { TpAccount *self = TP_ACCOUNT (proxy); + GSimpleAsyncResult *result = user_data; if (error != NULL) { @@ -3735,23 +3744,26 @@ _tp_account_got_all_addressing_cb (TpProxy *proxy, if (self->priv->uri_schemes == NULL) self->priv->uri_schemes = g_new0 (gchar *, 1); - _tp_proxy_set_feature_prepared (proxy, TP_ACCOUNT_FEATURE_ADDRESSING, TRUE); + g_simple_async_result_complete (result); } static void -tp_account_maybe_prepare_addressing (TpProxy *proxy) +tp_account_prepare_addressing_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpAccount *self = TP_ACCOUNT (proxy); + GSimpleAsyncResult *result; - if (self->priv->uri_schemes != NULL) - return; + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_account_prepare_addressing_async); - if (!_tp_proxy_is_preparing (proxy, TP_ACCOUNT_FEATURE_ADDRESSING)) - return; + g_assert (self->priv->uri_schemes == NULL); tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_ACCOUNT_INTERFACE_ADDRESSING, - _tp_account_got_all_addressing_cb, NULL, NULL, NULL); + _tp_account_got_all_addressing_cb, result, g_object_unref, NULL); } /** diff --git a/telepathy-glib/base-client-internal.h b/telepathy-glib/base-client-internal.h index a464b6a6..776e3a75 100644 --- a/telepathy-glib/base-client-internal.h +++ b/telepathy-glib/base-client-internal.h @@ -28,6 +28,9 @@ G_BEGIN_DECLS void _tp_base_client_set_only_for_account (TpBaseClient *self, TpAccount *account); +void _tp_base_client_now_handling_channels (TpBaseClient *self, + GPtrArray *channels); + G_END_DECLS #endif diff --git a/telepathy-glib/base-client.c b/telepathy-glib/base-client.c index b7ca1236..9f82b905 100644 --- a/telepathy-glib/base-client.c +++ b/telepathy-glib/base-client.c @@ -1922,14 +1922,14 @@ chan_invalidated_cb (TpChannel *channel, } static void -ctx_done_cb (TpHandleChannelsContext *context, - TpBaseClient *self) +add_handled_channels (TpBaseClient *self, + GPtrArray *channels) { guint i; - for (i = 0; i < context->channels->len; i++) + for (i = 0; i < channels->len; i++) { - TpChannel *channel = g_ptr_array_index (context->channels, i); + TpChannel *channel = g_ptr_array_index (channels, i); if (tp_proxy_get_invalidated (channel) == NULL) { @@ -1946,6 +1946,13 @@ ctx_done_cb (TpHandleChannelsContext *context, } static void +ctx_done_cb (TpHandleChannelsContext *context, + TpBaseClient *self) +{ + add_handled_channels (self, context->channels); +} + +static void handle_channels_context_prepare_cb (GObject *source, GAsyncResult *result, gpointer user_data) @@ -2785,3 +2792,16 @@ tp_base_client_is_handling_channel (TpBaseClient *self, g_list_free (channels); return found; } + +void +_tp_base_client_now_handling_channels (TpBaseClient *self, + GPtrArray *channels) +{ + g_return_if_fail (TP_IS_BASE_CLIENT (self)); + g_return_if_fail (channels != NULL); + + /* It only makes sense to update HandledChannels if the client is + * a Handler */ + if (self->priv->flags & CLIENT_IS_HANDLER) + add_handled_channels (self, channels); +} diff --git a/telepathy-glib/base-client.h b/telepathy-glib/base-client.h index 1925b66e..192a3222 100644 --- a/telepathy-glib/base-client.h +++ b/telepathy-glib/base-client.h @@ -30,7 +30,6 @@ #include <telepathy-glib/client-channel-factory.h> #include <telepathy-glib/handle-channels-context.h> #include <telepathy-glib/observe-channels-context.h> -#include <telepathy-glib/channel-dispatch-operation.h> #include <telepathy-glib/connection.h> #include <telepathy-glib/dbus.h> #include <telepathy-glib/defs.h> @@ -38,6 +37,10 @@ G_BEGIN_DECLS +/* forward declaration, see channel-dispatch-operation.h for the rest */ +typedef struct _TpChannelDispatchOperation + TpChannelDispatchOperation; + typedef struct _TpBaseClient TpBaseClient; typedef struct _TpBaseClientClass TpBaseClientClass; typedef struct _TpBaseClientPrivate TpBaseClientPrivate; diff --git a/telepathy-glib/channel-dispatch-operation.c b/telepathy-glib/channel-dispatch-operation.c index 61708838..1c9366d0 100644 --- a/telepathy-glib/channel-dispatch-operation.c +++ b/telepathy-glib/channel-dispatch-operation.c @@ -22,6 +22,7 @@ #include "telepathy-glib/channel-dispatch-operation.h" #include "telepathy-glib/channel-dispatch-operation-internal.h" +#include <telepathy-glib/base-client-internal.h> #include <telepathy-glib/channel.h> #include <telepathy-glib/defs.h> #include <telepathy-glib/errors.h> @@ -30,6 +31,7 @@ #include <telepathy-glib/proxy-internal.h> #include <telepathy-glib/proxy-subclass.h> #include <telepathy-glib/util.h> +#include <telepathy-glib/util-internal.h> #define DEBUG_FLAG TP_DEBUG_DISPATCHER #include "telepathy-glib/dbus-internal.h" @@ -114,8 +116,6 @@ struct _TpChannelDispatchOperationPrivate { GPtrArray *channels; GStrv possible_handlers; GHashTable *immutable_properties; - - gboolean preparing_core; }; enum @@ -587,12 +587,11 @@ get_dispatch_operation_prop_cb (TpProxy *proxy, GObject *weak_object) { TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) proxy; + GSimpleAsyncResult *result = user_data; gboolean prepared = TRUE; GPtrArray *channels; GError *e = NULL; - self->priv->preparing_core = FALSE; - if (error != NULL) { DEBUG ("Failed to fetch ChannelDispatchOperation properties: %s", @@ -667,8 +666,10 @@ get_dispatch_operation_prop_cb (TpProxy *proxy, g_object_notify ((GObject *) self, "cdo-properties"); out: - _tp_proxy_set_feature_prepared (proxy, - TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE, prepared); + if (e != NULL) + g_simple_async_result_set_from_error (result, e); + + g_simple_async_result_complete (result); if (!prepared) { @@ -678,26 +679,21 @@ out: } static void -maybe_prepare_core (TpProxy *proxy) +prepare_core_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) proxy; + GSimpleAsyncResult *result; - if (tp_proxy_is_prepared (proxy, TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE)) - return; /* already done */ - - if (self->priv->preparing_core) - return; /* already running */ - - if (!_tp_proxy_is_preparing (proxy, - TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE)) - return; /* not interested right now */ - - self->priv->preparing_core = TRUE; + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_core_async); tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_CHANNEL_DISPATCH_OPERATION, get_dispatch_operation_prop_cb, - NULL, NULL, NULL); + result, g_object_unref, NULL); } enum { @@ -715,7 +711,7 @@ tp_channel_dispatch_operation_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_CORE].name = TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE; features[FEAT_CORE].core = TRUE; - features[FEAT_CORE].start_preparing = maybe_prepare_core; + features[FEAT_CORE].prepare_async = prepare_core_async; /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -1207,15 +1203,17 @@ claim_cb (TpChannelDispatchOperation *self, * method becomes the handler for the channel. * * If successful, this method will cause the #TpProxy::invalidated signal - * to be emitted, in the same wayas for + * to be emitted, in the same way as for * tp_channel_dispatch_operation_handle_with_async(). * * This method may fail because the dispatch operation has already - * been completed. Again, see tp_channel_dispatch_operation_claim_async() + * been completed. Again, see tp_channel_dispatch_operation_handle_with_async() * for more details. The approver MUST NOT attempt to interact with * the channels further in this case. * * Since: 0.11.5 + * Deprecated: since 0.15.UNRELEASED. Use + * tp_channel_dispatch_operation_claim_with_async() */ void tp_channel_dispatch_operation_claim_async ( @@ -1245,6 +1243,8 @@ tp_channel_dispatch_operation_claim_async ( * Returns: %TRUE if the Claim() call was successful, otherwise %FALSE * * Since: 0.11.5 + * Deprecated: since 0.15.UNRELEASED. Use + * tp_channel_dispatch_operation_claim_with_finish() */ gboolean tp_channel_dispatch_operation_claim_finish ( @@ -1392,3 +1392,121 @@ gboolean return TRUE; } + +static void +claim_with_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) source; + GSimpleAsyncResult *main_result = user_data; + GError *error = NULL; + TpBaseClient *client; + + if (!tp_proxy_prepare_finish (self, result, &error)) + { + g_simple_async_result_take_error (main_result, error); + goto out; + } + + client = g_simple_async_result_get_op_res_gpointer (main_result); + + _tp_base_client_now_handling_channels (client, self->priv->channels); + +out: + g_simple_async_result_complete (main_result); + g_object_unref (main_result); +} + +static void +claim_with_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannelDispatchOperation *self = (TpChannelDispatchOperation *) source; + GSimpleAsyncResult *main_result = user_data; + GError *error = NULL; + GQuark features[] = { TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE, 0 }; + + if (!tp_channel_dispatch_operation_claim_finish (self, result, &error)) + { + g_simple_async_result_take_error (main_result, error); + g_simple_async_result_complete (main_result); + g_object_unref (main_result); + return; + } + + /* We have to prepare the CDO to be able to get the list of its channels */ + tp_proxy_prepare_async (self, features, claim_with_prepare_cb, + main_result); +} + +/** + * tp_channel_dispatch_operation_claim_with_async: + * @self: a #TpChannelDispatchOperation + * @client: the #TpBaseClient claiming @self + * @callback: a callback to call when the call returns + * @user_data: data to pass to @callback + * + * Called by an approver to claim channels for handling internally. + * If this method is called successfully, the process calling this + * method becomes the handler for the channel. + * + * If successful, this method will cause the #TpProxy::invalidated signal + * to be emitted, in the same way as for + * tp_channel_dispatch_operation_handle_with_async(). + * + * This method may fail because the dispatch operation has already + * been completed. Again, see tp_channel_dispatch_operation_handle_with_async() + * for more details. The approver MUST NOT attempt to interact with + * the channels further in this case. + * + * This is an improved version of tp_channel_dispatch_operation_claim_async() + * as it tells @client about the new channels being handled. + * + * Since: 0.15.UNRELEASED + */ +void +tp_channel_dispatch_operation_claim_with_async ( + TpChannelDispatchOperation *self, + TpBaseClient *client, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_CHANNEL_DISPATCH_OPERATION (self)); + + result = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, + tp_channel_dispatch_operation_claim_with_async); + + g_simple_async_result_set_op_res_gpointer (result, g_object_ref (client), + g_object_unref); + + tp_channel_dispatch_operation_claim_async (self, claim_with_cb, + result); +} + +/** + * tp_channel_dispatch_operation_claim_with_finish: + * @self: a #TpChannelDispatchOperation + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async call to Claim() initiated using + * tp_channel_dispatch_operation_claim_with_async(). + * + * Returns: %TRUE if the Claim() call was successful, otherwise %FALSE + * + * Since: 0.15.UNRELEASED + */ +gboolean +tp_channel_dispatch_operation_claim_with_finish ( + TpChannelDispatchOperation *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_void (self, \ + tp_channel_dispatch_operation_claim_with_async) +} diff --git a/telepathy-glib/channel-dispatch-operation.h b/telepathy-glib/channel-dispatch-operation.h index 30bd1110..605f98f0 100644 --- a/telepathy-glib/channel-dispatch-operation.h +++ b/telepathy-glib/channel-dispatch-operation.h @@ -23,6 +23,7 @@ #define TP_CHANNEL_DISPATCH_OPERATION_H #include <telepathy-glib/account.h> +#include <telepathy-glib/base-client.h> #include <telepathy-glib/connection.h> #include <telepathy-glib/dbus.h> #include <telepathy-glib/defs.h> @@ -30,8 +31,8 @@ G_BEGIN_DECLS -typedef struct _TpChannelDispatchOperation - TpChannelDispatchOperation; + +/* TpChannelDispatchOperation is defined in base-client.h */ typedef struct _TpChannelDispatchOperationClass TpChannelDispatchOperationClass; typedef struct _TpChannelDispatchOperationPrivate @@ -130,6 +131,17 @@ gboolean tp_channel_dispatch_operation_handle_with_time_finish ( GAsyncResult *result, GError **error); +void tp_channel_dispatch_operation_claim_with_async ( + TpChannelDispatchOperation *self, + TpBaseClient *client, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean tp_channel_dispatch_operation_claim_with_finish ( + TpChannelDispatchOperation *self, + GAsyncResult *result, + GError **error); + G_END_DECLS #include <telepathy-glib/_gen/tp-cli-channel-dispatch-operation.h> diff --git a/telepathy-glib/channel.c b/telepathy-glib/channel.c index 21bfc3fd..724b17ac 100644 --- a/telepathy-glib/channel.c +++ b/telepathy-glib/channel.c @@ -686,6 +686,7 @@ tp_channel_get_initial_chat_states_cb (TpProxy *proxy, GObject *weak_object) { TpChannel *self = TP_CHANNEL (proxy); + GSimpleAsyncResult *result = user_data; if (error == NULL && G_VALUE_HOLDS (value, TP_HASH_TYPE_CHAT_STATE_MAP)) { @@ -695,31 +696,22 @@ tp_channel_get_initial_chat_states_cb (TpProxy *proxy, /* else just ignore it and assume everyone was initially in the default * Inactive state, unless we already saw a signal for them */ - _tp_proxy_set_feature_prepared (proxy, TP_CHANNEL_FEATURE_CHAT_STATES, TRUE); + g_simple_async_result_complete (result); } static void -tp_channel_maybe_prepare_chat_states (TpProxy *proxy) +tp_channel_prepare_chat_states_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpChannel *self = (TpChannel *) proxy; + GSimpleAsyncResult *result; - if (self->priv->chat_states != NULL) - return; /* already done */ - - if (!_tp_proxy_is_preparing (proxy, TP_CHANNEL_FEATURE_CHAT_STATES)) - return; /* not interested right now */ - - if (!self->priv->ready) - return; /* will try again when ready */ + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_channel_prepare_chat_states_async); - if (!tp_proxy_has_interface_by_id (proxy, - TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE)) - { - /* not going to happen */ - _tp_proxy_set_feature_prepared (proxy, TP_CHANNEL_FEATURE_CHAT_STATES, - FALSE); - return; - } + g_assert (self->priv->chat_states == NULL); /* chat states? yes please! */ self->priv->chat_states = g_hash_table_new (NULL, NULL); @@ -730,7 +722,7 @@ tp_channel_maybe_prepare_chat_states (TpProxy *proxy) tp_cli_dbus_properties_call_get (self, -1, TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE, "ChatStates", tp_channel_get_initial_chat_states_cb, - NULL, NULL, NULL); + result, g_object_unref, NULL); } void @@ -764,8 +756,6 @@ _tp_channel_continue_introspection (TpChannel *self) TP_CHANNEL_FEATURE_GROUP, tp_proxy_has_interface_by_id (self, TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP)); - - tp_channel_maybe_prepare_chat_states ((TpProxy *) self); } else { @@ -1358,6 +1348,7 @@ static const TpProxyFeature * tp_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) { static TpProxyFeature features[N_FEAT + 1] = { { 0 } }; + static GQuark need_chat_states[2] = {0, 0}; if (G_LIKELY (features[0].name != 0)) return features; @@ -1368,8 +1359,10 @@ tp_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_GROUP].name = TP_CHANNEL_FEATURE_GROUP; features[FEAT_CHAT_STATES].name = TP_CHANNEL_FEATURE_CHAT_STATES; - features[FEAT_CHAT_STATES].start_preparing = - tp_channel_maybe_prepare_chat_states; + features[FEAT_CHAT_STATES].prepare_async = + tp_channel_prepare_chat_states_async; + need_chat_states[0] = TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE; + features[FEAT_CHAT_STATES].interfaces_needed = need_chat_states; /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); diff --git a/telepathy-glib/connection-avatars.c b/telepathy-glib/connection-avatars.c index 21028017..d75e3918 100644 --- a/telepathy-glib/connection-avatars.c +++ b/telepathy-glib/connection-avatars.c @@ -59,12 +59,12 @@ tp_connection_get_avatar_requirements_cb (TpProxy *proxy, GObject *weak_object) { TpConnection *self = (TpConnection *) proxy; - - self->priv->fetching_avatar_requirements = FALSE; + GSimpleAsyncResult *result = user_data; if (error != NULL) { DEBUG ("Failed to get avatar requirements properties: %s", error->message); + g_simple_async_result_set_from_error (result, error); goto finally; } @@ -83,40 +83,26 @@ tp_connection_get_avatar_requirements_cb (TpProxy *proxy, tp_asv_get_uint32 (properties, "MaximumAvatarBytes", NULL)); finally: - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS, - self->priv->avatar_requirements != NULL); + g_simple_async_result_complete (result); } void -_tp_connection_maybe_prepare_avatar_requirements (TpProxy *proxy) +_tp_connection_prepare_avatar_requirements_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpConnection *self = (TpConnection *) proxy; + GSimpleAsyncResult *result; - if (self->priv->avatar_requirements != NULL) - return; /* already done */ - - if (!_tp_proxy_is_preparing (proxy, TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS)) - return; /* not interested right now */ - - if (!self->priv->ready) - return; /* will try again when ready */ - - if (self->priv->fetching_avatar_requirements) - return; /* Another Get operation is running */ - - if (!tp_proxy_has_interface_by_id (proxy, - TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS)) - { - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS, - FALSE); - return; - } + g_assert (self->priv->avatar_requirements == NULL); - self->priv->fetching_avatar_requirements = TRUE; + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + _tp_connection_prepare_avatar_requirements_async); tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_CONNECTION_INTERFACE_AVATARS, - tp_connection_get_avatar_requirements_cb, NULL, NULL, NULL); + tp_connection_get_avatar_requirements_cb, result, g_object_unref, NULL); } /** diff --git a/telepathy-glib/connection-contact-info.c b/telepathy-glib/connection-contact-info.c index f684a0a6..cebbe93c 100644 --- a/telepathy-glib/connection-contact-info.c +++ b/telepathy-glib/connection-contact-info.c @@ -427,15 +427,15 @@ tp_connection_get_contact_info_cb (TpProxy *proxy, GObject *weak_object) { TpConnection *self = (TpConnection *) proxy; + GSimpleAsyncResult *result = user_data; GPtrArray *specs; gboolean valid; - gboolean success = FALSE; guint i; if (error != NULL) { DEBUG ("Failed to get contact info properties: %s", error->message); - goto finally; + g_simple_async_result_set_from_error (result, error); } g_assert (self->priv->contact_info_supported_fields == NULL); @@ -449,6 +449,8 @@ tp_connection_get_contact_info_cb (TpProxy *proxy, if (!valid || specs == NULL) { DEBUG ("Some properties are missing on the ContactInfo interface"); + g_simple_async_result_set_error (result, TP_ERRORS, TP_ERROR_CONFUSED, + "Some properties are missing on the ContactInfo interface"); goto finally; } @@ -468,44 +470,25 @@ tp_connection_get_contact_info_cb (TpProxy *proxy, _tp_contact_info_field_spec_new (name, parameters, flags, max)); } - success = TRUE; - finally: - - if (!success) - self->priv->contact_info_fetched = FALSE; - - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_CONTACT_INFO, - success); + g_simple_async_result_complete (result); } void -_tp_connection_maybe_prepare_contact_info (TpProxy *proxy) +_tp_connection_prepare_contact_info_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpConnection *self = (TpConnection *) proxy; + GSimpleAsyncResult *result; - if (self->priv->contact_info_fetched) - return; /* already done */ - - if (!_tp_proxy_is_preparing (proxy, TP_CONNECTION_FEATURE_CONTACT_INFO)) - return; /* not interested right now */ - - if (!self->priv->ready) - return; /* will try again when ready */ - - if (!tp_proxy_has_interface_by_id (proxy, - TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO)) - { - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_CONTACT_INFO, - FALSE); - return; - } - - self->priv->contact_info_fetched = TRUE; + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + _tp_connection_prepare_contact_info_async); tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, - tp_connection_get_contact_info_cb, NULL, NULL, NULL); + tp_connection_get_contact_info_cb, result, g_object_unref, NULL); } /** diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h index d4554195..92dddfa4 100644 --- a/telepathy-glib/connection-internal.h +++ b/telepathy-glib/connection-internal.h @@ -66,9 +66,6 @@ struct _TpConnectionPrivate { GList *contact_info_supported_fields; TpProxyPendingCall *introspection_call; - unsigned fetching_rcc:1; - unsigned fetching_avatar_requirements:1; - unsigned contact_info_fetched:1; unsigned ready:1; unsigned has_immortal_handles:1; @@ -103,12 +100,19 @@ TpContact *_tp_connection_lookup_contact (TpConnection *self, TpHandle handle); void _tp_contact_connection_invalidated (TpContact *contact); /* connection-contact-info.c */ -void _tp_connection_maybe_prepare_contact_info (TpProxy *proxy); +void _tp_connection_prepare_contact_info_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data); + TpContactInfoFieldSpec *_tp_contact_info_field_spec_new (const gchar *name, GStrv parameters, TpContactInfoFieldFlags flags, guint max); /* connection-avatars.c */ -void _tp_connection_maybe_prepare_avatar_requirements (TpProxy *proxy); +void _tp_connection_prepare_avatar_requirements_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data); G_END_DECLS diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c index f11f198a..faf243d5 100644 --- a/telepathy-glib/connection.c +++ b/telepathy-glib/connection.c @@ -297,8 +297,7 @@ tp_connection_get_rcc_cb (TpProxy *proxy, GObject *weak_object) { TpConnection *self = (TpConnection *) proxy; - - self->priv->fetching_rcc = FALSE; + GSimpleAsyncResult *result = user_data; if (error != NULL) { @@ -328,46 +327,74 @@ tp_connection_get_rcc_cb (TpProxy *proxy, FALSE); finally: - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_CAPABILITIES, - TRUE); + g_simple_async_result_complete (result); g_object_notify ((GObject *) self, "capabilities"); } static void -tp_connection_maybe_prepare_capabilities (TpProxy *proxy) +tp_connection_prepare_capabilities_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpConnection *self = (TpConnection *) proxy; + GSimpleAsyncResult *result; - if (self->priv->capabilities != NULL) - return; /* already done */ + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_connection_prepare_capabilities_async); - if (!_tp_proxy_is_preparing (proxy, TP_CONNECTION_FEATURE_CAPABILITIES)) - return; /* not interested right now */ + g_assert (self->priv->capabilities == NULL); - if (!self->priv->ready) - return; /* will try again when ready */ + tp_cli_dbus_properties_call_get (self, -1, + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, "RequestableChannelClasses", + tp_connection_get_rcc_cb, result, g_object_unref, NULL); +} - if (self->priv->fetching_rcc) - return; /* Another Get operation is running */ +static void +signal_connected (TpConnection *self) +{ + /* we shouldn't have gone to status CONNECTED for any reason + * that isn't REQUESTED :-) */ + DEBUG ("%p: CORE and CONNECTED ready", self); + self->priv->status = TP_CONNECTION_STATUS_CONNECTED; + self->priv->status_reason = TP_CONNECTION_STATUS_REASON_REQUESTED; + self->priv->ready = TRUE; + + _tp_proxy_set_feature_prepared ((TpProxy *) self, + TP_CONNECTION_FEATURE_CONNECTED, TRUE); + _tp_proxy_set_feature_prepared ((TpProxy *) self, + TP_CONNECTION_FEATURE_CORE, TRUE); + + g_object_notify ((GObject *) self, "status"); + g_object_notify ((GObject *) self, "status-reason"); + g_object_notify ((GObject *) self, "connection-ready"); +} - if (!tp_proxy_has_interface_by_id (proxy, - TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS)) +static void +will_announced_connected_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpConnection *self = (TpConnection *) source; + GError *error = NULL; + + if (!_tp_proxy_will_announce_connected_finish ((TpProxy *) self, result, + &error)) { - /* Connection doesn't support Requests; set an empty TpCapabilities - * object as all calls to CreateChannel/EnsureChannel will fail. */ + DEBUG ("_tp_connection_prepare_contact_info_async failed: %s", + error->message); - self->priv->capabilities = _tp_capabilities_new (NULL, FALSE); - _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_CAPABILITIES, - TRUE); - return; + g_error_free (error); } - self->priv->fetching_rcc = TRUE; + if (tp_proxy_get_invalidated (self) != NULL) + { + DEBUG ("Connection has been invalidated; we're done"); + return; + } - tp_cli_dbus_properties_call_get (self, -1, - TP_IFACE_CONNECTION_INTERFACE_REQUESTS, "RequestableChannelClasses", - tp_connection_get_rcc_cb, NULL, NULL, NULL); + signal_connected (self); } static void @@ -390,25 +417,10 @@ tp_connection_continue_introspection (TpConnection *self) return; } - /* signal CONNECTED; we shouldn't have gone to status CONNECTED for any - * reason that isn't REQUESTED :-) */ - DEBUG ("%p: CORE and CONNECTED ready", self); - self->priv->status = TP_CONNECTION_STATUS_CONNECTED; - self->priv->status_reason = TP_CONNECTION_STATUS_REASON_REQUESTED; - self->priv->ready = TRUE; - - _tp_proxy_set_feature_prepared ((TpProxy *) self, - TP_CONNECTION_FEATURE_CONNECTED, TRUE); - _tp_proxy_set_feature_prepared ((TpProxy *) self, - TP_CONNECTION_FEATURE_CORE, TRUE); - - g_object_notify ((GObject *) self, "status"); - g_object_notify ((GObject *) self, "status-reason"); - g_object_notify ((GObject *) self, "connection-ready"); - - tp_connection_maybe_prepare_capabilities ((TpProxy *) self); - _tp_connection_maybe_prepare_avatar_requirements ((TpProxy *) self); - _tp_connection_maybe_prepare_contact_info ((TpProxy *) self); + /* We'll announce CONNECTED state soon, but first give a chance to + * prepared feature to be updated, if needed */ + _tp_proxy_will_announce_connected_async ((TpProxy *) self, + will_announced_connected_cb, NULL); } else { @@ -1262,6 +1274,9 @@ static const TpProxyFeature * tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED) { static TpProxyFeature features[N_FEAT + 1] = { { 0 } }; + static GQuark need_requests[2] = {0, 0}; + static GQuark need_avatars[2] = {0, 0}; + static GQuark need_contact_info[2] = {0, 0}; if (G_LIKELY (features[0].name != 0)) return features; @@ -1272,16 +1287,22 @@ tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_CONNECTED].name = TP_CONNECTION_FEATURE_CONNECTED; features[FEAT_CAPABILITIES].name = TP_CONNECTION_FEATURE_CAPABILITIES; - features[FEAT_CAPABILITIES].start_preparing = - tp_connection_maybe_prepare_capabilities; + features[FEAT_CAPABILITIES].prepare_async = + tp_connection_prepare_capabilities_async; + need_requests[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS; + features[FEAT_CAPABILITIES].interfaces_needed = need_requests; features[FEAT_AVATAR_REQUIREMENTS].name = TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS; - features[FEAT_AVATAR_REQUIREMENTS].start_preparing = - _tp_connection_maybe_prepare_avatar_requirements; + features[FEAT_AVATAR_REQUIREMENTS].prepare_async = + _tp_connection_prepare_avatar_requirements_async; + need_avatars[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS; + features[FEAT_AVATAR_REQUIREMENTS].interfaces_needed = need_avatars; features[FEAT_CONTACT_INFO].name = TP_CONNECTION_FEATURE_CONTACT_INFO; - features[FEAT_CONTACT_INFO].start_preparing = - _tp_connection_maybe_prepare_contact_info; + features[FEAT_CONTACT_INFO].prepare_async = + _tp_connection_prepare_contact_info_async; + need_contact_info[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO; + features[FEAT_CONTACT_INFO].interfaces_needed = need_contact_info; /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); diff --git a/telepathy-glib/group-mixin.c b/telepathy-glib/group-mixin.c index 8120b98b..e2123e75 100644 --- a/telepathy-glib/group-mixin.c +++ b/telepathy-glib/group-mixin.c @@ -1390,7 +1390,7 @@ local_pending_remove (TpGroupMixin *mixin, static void -add_members_in_array (GHashTable *member_ids, +add_members_in_array (GHashTable *contact_ids, TpHandleRepoIface *repo, const GArray *handles) { @@ -1401,61 +1401,61 @@ add_members_in_array (GHashTable *member_ids, TpHandle handle = g_array_index (handles, TpHandle, i); const gchar *id = tp_handle_inspect (repo, handle); - g_hash_table_insert (member_ids, GUINT_TO_POINTER (handle), (gchar *) id); + g_hash_table_insert (contact_ids, GUINT_TO_POINTER (handle), (gchar *) id); } } static gboolean -maybe_add_member_ids (TpGroupMixin *mixin, +maybe_add_contact_ids (TpGroupMixin *mixin, const GArray *add, const GArray *local_pending, const GArray *remote_pending, TpHandle actor, GHashTable *details) { - GHashTable *member_ids; + GHashTable *contact_ids; /* If the library user had its own ideas about which members' IDs to include * in the change details, we'll leave that intact. */ - if (tp_asv_lookup (details, "member-ids") != NULL) + if (tp_asv_lookup (details, "contact-ids") != NULL) return FALSE; /* The library user didn't include the new members' IDs in details; let's add * the IDs of the handles being added to the group (but not removed, as per * the spec) and of the actor. */ - member_ids = g_hash_table_new (NULL, NULL); + contact_ids = g_hash_table_new (NULL, NULL); - add_members_in_array (member_ids, mixin->handle_repo, add); - add_members_in_array (member_ids, mixin->handle_repo, local_pending); - add_members_in_array (member_ids, mixin->handle_repo, remote_pending); + add_members_in_array (contact_ids, mixin->handle_repo, add); + add_members_in_array (contact_ids, mixin->handle_repo, local_pending); + add_members_in_array (contact_ids, mixin->handle_repo, remote_pending); if (actor != 0) { const gchar *id = tp_handle_inspect (mixin->handle_repo, actor); - g_hash_table_insert (member_ids, GUINT_TO_POINTER (actor), (gchar *) id); + g_hash_table_insert (contact_ids, GUINT_TO_POINTER (actor), (gchar *) id); } - g_hash_table_insert (details, "member-ids", + g_hash_table_insert (details, "contact-ids", tp_g_value_slice_new_take_boxed (TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP, - member_ids)); + contact_ids)); return TRUE; } static void -remove_member_ids (GHashTable *details) +remove_contact_ids (GHashTable *details) { - GValue *member_ids_v = g_hash_table_lookup (details, "member-ids"); + GValue *contact_ids_v = g_hash_table_lookup (details, "contact-ids"); - g_assert (member_ids_v != NULL); - g_hash_table_steal (details, "member-ids"); + g_assert (contact_ids_v != NULL); + g_hash_table_steal (details, "contact-ids"); - tp_g_value_slice_free (member_ids_v); + tp_g_value_slice_free (contact_ids_v); } @@ -1472,7 +1472,7 @@ emit_members_changed_signals (GObject *channel, { TpGroupMixin *mixin = TP_GROUP_MIXIN (channel); GHashTable *details_ = (GHashTable *) details; /* Cast the pain away! */ - gboolean added_member_ids; + gboolean added_contact_ids; if (DEBUGGING) { @@ -1502,7 +1502,7 @@ emit_members_changed_signals (GObject *channel, g_free (remote_str); } - added_member_ids = maybe_add_member_ids (mixin, add, local_pending, + added_contact_ids = maybe_add_contact_ids (mixin, add, local_pending, remote_pending, actor, details_); tp_svc_channel_interface_group_emit_members_changed (channel, message, @@ -1525,8 +1525,8 @@ emit_members_changed_signals (GObject *channel, } } - if (added_member_ids) - remove_member_ids (details_); + if (added_contact_ids) + remove_contact_ids (details_); } diff --git a/telepathy-glib/proxy-internal.h b/telepathy-glib/proxy-internal.h index 949ba233..4fce531f 100644 --- a/telepathy-glib/proxy-internal.h +++ b/telepathy-glib/proxy-internal.h @@ -27,16 +27,6 @@ GError *_tp_proxy_take_and_remap_error (TpProxy *self, GError *error) typedef void (*TpProxyProc) (TpProxy *); -struct _TpProxyFeature { - /*<public>*/ - GQuark name; - gboolean core; - TpProxyProc start_preparing; - /*<private>*/ - GCallback _reserved[4]; - gpointer priv; -}; - gboolean _tp_proxy_is_preparing (gpointer self, GQuark feature); void _tp_proxy_set_feature_prepared (TpProxy *self, @@ -45,4 +35,12 @@ void _tp_proxy_set_feature_prepared (TpProxy *self, void _tp_proxy_set_features_failed (TpProxy *self, const GError *error); +void _tp_proxy_will_announce_connected_async (TpProxy *self, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean _tp_proxy_will_announce_connected_finish (TpProxy *self, + GAsyncResult *result, + GError **error); + #endif diff --git a/telepathy-glib/proxy.c b/telepathy-glib/proxy.c index f80dcf7a..b64e9190 100644 --- a/telepathy-glib/proxy.c +++ b/telepathy-glib/proxy.c @@ -208,10 +208,36 @@ tp_dbus_errors_quark (void) */ /** + * TpProxyPrepareAsync: + * @proxy: the object on which @feature has to be prepared + * @feature: a #GQuark representing the feature to prepare + * @callback: called when the feature has been prepared, or the preparation + * failed + * @user_data: data to pass to @callback + * + * Function called when @feature has to be prepared for @proxy. + */ + +/** * TpProxyFeature: - * - * Structure representing a feature. This is currently opaque to code outside - * telepathy-glib itself. + * @name: a #GQuark representing the name of the feature + * @core: if %TRUE, every non-core feature of the class depends on this one, + * and every feature (core or not) in subclasses depends on this one + * @prepare_async: called when the feature has to be prepared + * @prepare_before_signalling_connected_async: only relevant for + * TpConnection sub-classes; same as @prepare_async but for + * features wanting to have a chance to prepare themself before the + * TpConnection object announce its %TP_CONNECTION_STATUS_CONNECTED status + * @interfaces_needed: an array of #GQuark representing interfaces which have + * to be implemented on the object in order to be able to prepare the feature + * @depends_on: an array of #GQuark representing other features which have to + * be prepared before trying to prepare this feature + * @can_retry: If %TRUE, allow retrying preparation of this feature even if it + * failed once already; if %FALSE any attempt of preparing the feature after + * the preparation already failed once will immediately fail with re-calling + * @prepare_async + * + * Structure representing a feature. * * Since: 0.11.3 */ @@ -245,6 +271,11 @@ struct _TpProxyInterfaceAddLink { TpProxyInterfaceAddLink *next; }; +struct _TpProxyFeaturePrivate +{ + gpointer unused; +}; + /** * TpProxyInvokeFunc: * @self: the #TpProxy on which the D-Bus method was invoked @@ -280,16 +311,25 @@ struct _TpProxyInterfaceAddLink { */ typedef enum { + /* Not a feature */ FEATURE_STATE_INVALID = GPOINTER_TO_INT (NULL), + /* Nobody cares */ FEATURE_STATE_UNWANTED, + /* Want to prepare, waiting for dependencies to be satisfied (or maybe + * just poll_features being called) */ FEATURE_STATE_WANTED, + /* Want to prepare, have called prepare_async */ + FEATURE_STATE_TRYING, + /* Couldn't prepare, gave up */ FEATURE_STATE_FAILED, + /* Prepared */ FEATURE_STATE_READY } FeatureState; typedef struct { GSimpleAsyncResult *result; GArray *features; + gboolean core; } TpProxyPrepareRequest; static TpProxyPrepareRequest * @@ -302,6 +342,7 @@ tp_proxy_prepare_request_new (GSimpleAsyncResult *result, req->result = g_object_ref (result); req->features = _tp_quark_array_copy (features); + g_assert (req->features != NULL); return req; } @@ -333,12 +374,16 @@ struct _TpProxyPrivate { /* feature => FeatureState */ GData *features; - /* List of TpProxyPrepareRequest */ - GList *prepare_requests; - /* A request containing all core features, borrowed from the head of - * prepare_requests, or NULL if prepared; nothing else is allowed - * to become prepared until this one does.*/ - TpProxyPrepareRequest *prepare_core; + /* Queue of TpProxyPrepareRequest. The first requests are the core one, + * sorted from the most upper super class to the subclass core features. + * This is needed to guarantee than subclass features are not prepared + * until the super class features have been prepared. */ + GQueue *prepare_requests; + + GSimpleAsyncResult *will_announce_connected_result; + /* Number of pending calls blocking will_announce_connected_result to be + * completed */ + guint pending_will_announce_calls; gboolean dispose_has_run; }; @@ -524,7 +569,7 @@ tp_proxy_emit_invalidated (gpointer p) /* make all pending tp_proxy_prepare_async calls fail */ tp_proxy_poll_features (self, NULL); - g_assert (self->priv->prepare_requests == NULL); + g_assert_cmpuint (g_queue_get_length (self->priv->prepare_requests), ==, 0); /* Don't clear the datalist until after we've emitted the signal, so * the pending call and signal connection friend classes can still get @@ -907,6 +952,8 @@ tp_proxy_init (TpProxy *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_PROXY, TpProxyPrivate); + + self->priv->prepare_requests = g_queue_new (); } static GQuark @@ -939,6 +986,22 @@ tp_proxy_set_feature_state (TpProxy *self, GINT_TO_POINTER (state)); } +static void +assert_feature_validity (TpProxy *self, + const TpProxyFeature *feature) +{ + g_assert (feature != NULL); + + /* Core features can't have depends, their depends are implicit */ + if (feature->core) + g_assert (feature->depends_on == NULL || feature->depends_on[0] == 0); + + /* prepare_before_signalling_connected_async only make sense for + * TpConnection subclasses */ + if (feature->prepare_before_signalling_connected_async != NULL) + g_assert (TP_IS_CONNECTION (self)); +} + static GObject * tp_proxy_constructor (GType type, guint n_params, @@ -951,12 +1014,9 @@ tp_proxy_constructor (GType type, TpProxyInterfaceAddLink *iter; GType proxy_parent_type = G_TYPE_FROM_CLASS (tp_proxy_parent_class); GType ancestor_type; - GArray *core_features; _tp_register_dbus_glib_marshallers (); - core_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); - for (ancestor_type = type; ancestor_type != proxy_parent_type && ancestor_type != 0; ancestor_type = g_type_parent (ancestor_type)) @@ -964,6 +1024,7 @@ tp_proxy_constructor (GType type, TpProxyClass *ancestor = g_type_class_peek (ancestor_type); const TpProxyFeature *features; guint i; + GArray *core_features; for (iter = g_type_get_qdata (ancestor_type, interface_added_cb_quark ()); @@ -980,8 +1041,12 @@ tp_proxy_constructor (GType type, if (features == NULL) continue; + core_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + for (i = 0; features[i].name != 0; i++) { + assert_feature_validity (self, &features[i]); + tp_proxy_set_feature_state (self, features[i].name, FEATURE_STATE_UNWANTED); @@ -990,6 +1055,22 @@ tp_proxy_constructor (GType type, g_array_append_val (core_features, features[i].name); } } + + if (core_features->len > 0) + { + TpProxyPrepareRequest *req; + + req = tp_proxy_prepare_request_new (NULL, + (const GQuark *) core_features->data); + req->core = TRUE; + + g_queue_push_head (self->priv->prepare_requests, req); + + DEBUG ("%p: request %p represents core features on %s", self, req, + g_type_name (ancestor_type)); + } + + g_array_free (core_features, TRUE); } g_return_val_if_fail (self->dbus_connection != NULL, NULL); @@ -1017,19 +1098,6 @@ tp_proxy_constructor (GType type, g_return_val_if_fail (self->bus_name[0] == ':', NULL); } - if (core_features->len > 0) - { - self->priv->prepare_core = tp_proxy_prepare_request_new (NULL, - (const GQuark *) core_features->data); - self->priv->prepare_requests = g_list_prepend ( - self->priv->prepare_requests, - self->priv->prepare_core); - DEBUG ("%p: request %p represents core features", self, - self->priv->prepare_core); - } - - g_array_free (core_features, TRUE); - return (GObject *) self; } @@ -1069,8 +1137,8 @@ tp_proxy_finalize (GObject *object) g_error_free (self->invalidated); /* invalidation ensures that these have gone away */ - g_assert (self->priv->prepare_requests == NULL); - g_assert (self->priv->prepare_core == NULL); + g_assert_cmpuint (g_queue_get_length (self->priv->prepare_requests), ==, 0); + tp_clear_pointer (&self->priv->prepare_requests, g_queue_free); g_free (self->bus_name); g_free (self->object_path); @@ -1580,7 +1648,126 @@ _tp_proxy_is_preparing (gpointer self, state = tp_proxy_get_feature_state (self, feature); g_return_val_if_fail (state != FEATURE_STATE_INVALID, FALSE); - return (state == FEATURE_STATE_WANTED); + return (state == FEATURE_STATE_WANTED || state == FEATURE_STATE_TRYING); +} + +static gboolean +check_feature_interfaces (TpProxy *self, + GQuark name) +{ + const TpProxyFeature *feature = tp_proxy_subclass_get_feature ( + G_OBJECT_TYPE (self), name); + guint i; + + if (feature->interfaces_needed == NULL) + return TRUE; + + for (i = 0; feature->interfaces_needed[i] != 0; i++) + { + if (!tp_proxy_has_interface_by_id (self, feature->interfaces_needed[i])) + { + DEBUG ("Proxy doesn't implement %s, can't prepare feature %s", + g_quark_to_string (feature->interfaces_needed[i]), + g_quark_to_string (name)); + + return FALSE; + } + } + + return TRUE; +} + +/* Returns %TRUE if all the deps of @name are ready + * @can_retry: if %TRUE dependencies which have failed but have + * TpProxyFeature.can_retry won't be considered as having failed so we'll + * still have a change to retry preparing those. + * @failed: (out): %TRUE if one of @name's dep can't be prepared and so + * @name can't be either + */ +static gboolean +check_depends_ready (TpProxy *self, + GQuark name, + gboolean can_retry, + gboolean *failed) +{ + const TpProxyFeature *feature = tp_proxy_subclass_get_feature ( + G_OBJECT_TYPE (self), name); + guint i; + gboolean ready = TRUE; + + g_assert (failed != NULL); + *failed = FALSE; + + if (feature->depends_on == NULL) + return TRUE; + + for (i = 0; feature->depends_on[i] != 0; i++) + { + GQuark dep = feature->depends_on[i]; + const TpProxyFeature *dep_feature = tp_proxy_subclass_get_feature ( + G_OBJECT_TYPE (self), dep); + FeatureState dep_state; + + dep_state = tp_proxy_get_feature_state (self, dep); + switch (dep_state) + { + case FEATURE_STATE_INVALID: + DEBUG ("Can't prepare %s, because %s (a dependency) is " + "invalid", g_quark_to_string (name), g_quark_to_string (dep)); + + *failed = TRUE; + return FALSE; + + case FEATURE_STATE_FAILED: + if (!can_retry || !dep_feature->can_retry) + { + DEBUG ("Can't prepare %s, because %s (a dependency) is " + "failed to prepare", + g_quark_to_string (name), g_quark_to_string (dep)); + + *failed = TRUE; + return FALSE; + } + + DEBUG ("retry preparing dep: %s", g_quark_to_string (dep)); + tp_proxy_set_feature_state (self, dep, FEATURE_STATE_WANTED); + ready = FALSE; + break; + + case FEATURE_STATE_UNWANTED: + case FEATURE_STATE_WANTED: + case FEATURE_STATE_TRYING: + ready = FALSE; + break; + + case FEATURE_STATE_READY: + break; + } + } + + return ready; +} + +static void +depends_prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpProxy *self = (TpProxy *) source; + + tp_proxy_poll_features (self, NULL); +} + +static void +prepare_depends (TpProxy *self, + GQuark name) +{ + const TpProxyFeature *feature; + + feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name); + g_assert (feature->depends_on != NULL); + + tp_proxy_prepare_async (self, feature->depends_on, depends_prepare_cb, NULL); } /** @@ -1649,7 +1836,6 @@ tp_proxy_prepare_async (gpointer self, { TpProxy *proxy = self; GSimpleAsyncResult *result = NULL; - const GError *error; guint i; g_return_if_fail (TP_IS_PROXY (self)); @@ -1660,26 +1846,39 @@ tp_proxy_prepare_async (gpointer self, for (i = 0; features[i] != 0; i++) { FeatureState state = tp_proxy_get_feature_state (self, features[i]); + const TpProxyFeature *feature = tp_proxy_subclass_get_feature ( + G_OBJECT_TYPE (self), features[i]); + /* We just skip unknown features, which have state FEATURE_STATE_INVALID + * (this doesn't seem ideal, but is + * consistent with TpAccountManager's existing behaviour) */ if (state == FEATURE_STATE_INVALID) { - /* just skip unknown features (this doesn't seem ideal, but is - * consistent with TpAccountManager's existing behaviour) */ continue; } - - if (state == FEATURE_STATE_UNWANTED) + else if (state == FEATURE_STATE_UNWANTED || + (state == FEATURE_STATE_FAILED && feature->can_retry)) { - const TpProxyFeature *feat_struct = tp_proxy_subclass_get_feature ( - G_OBJECT_TYPE (self), features[i]); + gboolean failed; - g_return_if_fail (feat_struct != NULL); + /* Check deps. We only offer there the chance to retry a previously + * failed dependency. Doing it in tp_proxy_poll_features() could + * result in an infinite loop if we'd depends on 2 features which + * are constantly failing. */ + if (!check_depends_ready (self, features[i], TRUE, &failed)) + { + if (failed) + { + /* We can't prepare the feature because of its deps */ + tp_proxy_set_feature_state (self, features[i], + FEATURE_STATE_FAILED); + continue; + } - DEBUG ("%p: %s newly wanted", self, g_quark_to_string (features[i])); - tp_proxy_set_feature_state (self, features[i], FEATURE_STATE_WANTED); + prepare_depends (self, features[i]); + } - if (feat_struct->start_preparing != NULL) - feat_struct->start_preparing (self); + tp_proxy_set_feature_state (self, features[i], FEATURE_STATE_WANTED); } } @@ -1687,21 +1886,18 @@ tp_proxy_prepare_async (gpointer self, result = g_simple_async_result_new (self, callback, user_data, tp_proxy_prepare_async); - error = tp_proxy_get_invalidated (self); - - if (error != NULL) + if (proxy->invalidated != NULL) { if (result != NULL) { - g_simple_async_result_set_from_error (result, error); + g_simple_async_result_set_from_error (result, proxy->invalidated); g_simple_async_result_complete_in_idle (result); } goto finally; } - proxy->priv->prepare_requests = g_list_append ( - proxy->priv->prepare_requests, + g_queue_push_tail (proxy->priv->prepare_requests, tp_proxy_prepare_request_new (result, features)); tp_proxy_poll_features (proxy, NULL); @@ -1745,11 +1941,167 @@ tp_proxy_prepare_finish (gpointer self, return TRUE; } +static gboolean +prepare_finish (TpProxy *self, + GAsyncResult *result, + gpointer source, + GError **error) +{ + _tp_implement_finish_void (self, source); +} + +static void +feature_prepared_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpProxy *self = (TpProxy *) source; + TpProxyFeature *feature = user_data; + GError *error = NULL; + gboolean prepared = TRUE; + + if (!prepare_finish (self, result, feature->prepare_async, &error)) + { + DEBUG ("Failed to prepare %s: %s", g_quark_to_string (feature->name), + error->message); + + prepared = FALSE; + g_error_free (error); + } + + _tp_proxy_set_feature_prepared (self, feature->name, prepared); +} + +static void +prepare_feature (TpProxy *self, + const TpProxyFeature *feature) +{ + if (feature->prepare_async == NULL) + return; + + feature->prepare_async (self, feature, feature_prepared_cb, + (gpointer) feature); +} + +static gboolean +core_prepared (TpProxy *self) +{ + /* All the core features have been prepared if the head of the + * prepare_requests queue is NOT a core feature */ + TpProxyPrepareRequest *req = g_queue_peek_head (self->priv->prepare_requests); + + if (req == NULL) + return TRUE; + + return !req->core; +} + +/* Returns %TRUE if all the features requested in @req have complete their + * preparation */ +static gboolean +request_is_complete (TpProxy *self, + TpProxyPrepareRequest *req) +{ + guint i; + gboolean complete = TRUE; + + for (i = 0; i < req->features->len; i++) + { + GQuark feature = g_array_index (req->features, GQuark, i); + FeatureState state = tp_proxy_get_feature_state (self, feature); + const TpProxyFeature *feat_struct = tp_proxy_subclass_get_feature ( + G_OBJECT_TYPE (self), feature); + + switch (state) + { + case FEATURE_STATE_UNWANTED: + /* this can only happen in the special pseudo-request for the + * core features, which blocks everything */ + g_assert (req->core); + complete = FALSE; + + /* fall through to treat it as WANTED */ + case FEATURE_STATE_WANTED: + if (core_prepared (self) || + req->core) + { + gboolean failed; + + /* Check if we have the required interfaces. We can't do that + * in tp_proxy_prepare_async() as CORE have to be prepared */ + if (!check_feature_interfaces (self, feature)) + { + tp_proxy_set_feature_state (self, feature, + FEATURE_STATE_FAILED); + continue; + } + + if (check_depends_ready (self, feature, FALSE, &failed)) + { + /* We can prepare it now */ + DEBUG ("%p: calling callback for %s", self, + g_quark_to_string (feature)); + + tp_proxy_set_feature_state (self, feature, + FEATURE_STATE_TRYING); + + prepare_feature (self, feat_struct); + complete = FALSE; + } + else if (failed) + { + tp_proxy_set_feature_state (self, feature, + FEATURE_STATE_FAILED); + } + else + { + /* We have to wait until the deps finish their + * preparation. */ + complete = FALSE; + } + } + break; + + case FEATURE_STATE_TRYING: + complete = FALSE; + break; + + case FEATURE_STATE_INVALID: + case FEATURE_STATE_FAILED: + case FEATURE_STATE_READY: + /* nothing more to do */ + break; + } + } + + return complete; +} + +static void +finish_all_requests (TpProxy *self, + const GError *error) +{ + GList *iter; + GQueue *tmp = g_queue_copy (self->priv->prepare_requests); + + g_queue_clear (self->priv->prepare_requests); + + for (iter = tmp->head; iter != NULL; iter = g_list_next (iter)) + { + tp_proxy_prepare_request_finish (iter->data, error); + } + + g_queue_clear (tmp); +} + /* * tp_proxy_poll_features: * @self: a proxy * @error: if not %NULL, fail all feature requests with this error * + * For each feature in state WANTED, if its dependencies have been satisfied, + * call the callback and advance it to state TRYING. + * * For each feature request, see if it's finished yet. * * Called every time the set of prepared/failed features changes, @@ -1766,71 +2118,53 @@ tp_proxy_poll_features (TpProxy *self, GList *iter; GList *next; - if (self->priv->prepare_requests == NULL) + if (g_queue_get_length (self->priv->prepare_requests) == 0) return; - if (error == NULL) - { - error_source = "invalidated"; - error = self->invalidated; - } + g_object_ref (self); - if (error != NULL) + for (iter = self->priv->prepare_requests->head; iter != NULL; iter = next) { - DEBUG ("%p: %s, ending all requests", self, error_source); - iter = self->priv->prepare_requests; - self->priv->prepare_core = NULL; - self->priv->prepare_requests = NULL; + TpProxyPrepareRequest *req = iter->data; + TpProxyPrepareRequest *head = g_queue_peek_head ( + self->priv->prepare_requests); - for ( ; iter != NULL; iter = g_list_delete_link (iter, iter)) + if (error == NULL) { - tp_proxy_prepare_request_finish (iter->data, error); + error_source = "invalidated"; + error = self->invalidated; } - } - for (iter = self->priv->prepare_requests; iter != NULL; iter = next) - { - TpProxyPrepareRequest *req = iter->data; - gboolean wait = FALSE; - guint i; + if (error != NULL) + { + DEBUG ("%p: %s, ending all requests", self, error_source); + + finish_all_requests (self, error); + break; + } next = iter->next; - /* prepare_core is always the first in the list (if present), so it - * will always have been checked by the time we reach any later one */ - if (self->priv->prepare_core != NULL && req != self->priv->prepare_core) + /* Core features have to be prepared first, in superclass-to-subclass + * order. The next core feature to be prepared, if any, is always at the + * head of prepare_requests. */ + if (!core_prepared (self) && + req != head) { DEBUG ("%p: core features not ready yet, nothing prepared", self); continue; } - for (i = 0; i < req->features->len; i++) - { - FeatureState state = tp_proxy_get_feature_state (self, - g_array_index (req->features, GQuark, i)); - - if (state == FEATURE_STATE_UNWANTED || state == FEATURE_STATE_WANTED) - { - wait = TRUE; - break; - } - } - - if (!wait) + if (request_is_complete (self, req)) { DEBUG ("%p: request %p prepared", self, req); - self->priv->prepare_requests = g_list_delete_link ( - self->priv->prepare_requests, iter); - - if (req == self->priv->prepare_core) - { - DEBUG ("%p: core features ready", self); - self->priv->prepare_core = NULL; - } + g_queue_delete_link (self->priv->prepare_requests, iter); tp_proxy_prepare_request_finish (req, NULL); } } + + g_object_unref (self); } /* @@ -1885,3 +2219,91 @@ _tp_proxy_set_features_failed (TpProxy *self, g_return_if_fail (error != NULL); tp_proxy_poll_features (self, error); } + +static void +check_announce_connected (TpProxy *self, + gboolean in_idle) +{ + if (self->priv->pending_will_announce_calls != 0) + return; + + if (in_idle) + { + g_simple_async_result_complete_in_idle ( + self->priv->will_announce_connected_result); + } + else + { + g_simple_async_result_complete ( + self->priv->will_announce_connected_result); + } + + tp_clear_object (&self->priv->will_announce_connected_result); +} + +static void +prepare_before_signalling_connected_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpProxy *self = user_data; + + /* We don't care if the call succeeded or not as it was already prepared */ + self->priv->pending_will_announce_calls--; + + check_announce_connected (self, FALSE); +} + +static void foreach_feature (GQuark name, + gpointer data, + gpointer user_data) +{ + FeatureState state = GPOINTER_TO_INT (data); + TpProxy *self = user_data; + const TpProxyFeature *feature; + + if (state != FEATURE_STATE_READY) + return; + + feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name); + + if (feature->prepare_before_signalling_connected_async == NULL) + return; + + self->priv->pending_will_announce_calls++; + + feature->prepare_before_signalling_connected_async (self, feature, + prepare_before_signalling_connected_cb, self); +} + +/* + * _tp_proxy_will_announce_connected_async: + * + * Called by connection.c when the connection became connected and we're about + * to announce it. But before we have to wait for all the prepared features to + * process their prepare_before_signalling_connected_async, if any. + */ +void +_tp_proxy_will_announce_connected_async (TpProxy *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_assert (TP_IS_CONNECTION (self)); + g_assert (self->priv->will_announce_connected_result == NULL); + + self->priv->will_announce_connected_result = g_simple_async_result_new ( + (GObject *) self, callback, user_data, + _tp_proxy_will_announce_connected_async); + + g_datalist_foreach (&self->priv->features, foreach_feature, self); + + check_announce_connected (self, TRUE); +} + +gboolean +_tp_proxy_will_announce_connected_finish (TpProxy *self, + GAsyncResult *result, + GError **error) +{ + _tp_implement_finish_void (self, _tp_proxy_will_announce_connected_async) +} diff --git a/telepathy-glib/proxy.h b/telepathy-glib/proxy.h index 0f350d8d..8b0cdfb3 100644 --- a/telepathy-glib/proxy.h +++ b/telepathy-glib/proxy.h @@ -71,8 +71,32 @@ struct _TpProxy { typedef struct _TpProxyClass TpProxyClass; -/* defined in proxy-internal.h for now */ typedef struct _TpProxyFeature TpProxyFeature; +typedef struct _TpProxyFeaturePrivate TpProxyFeaturePrivate; + +typedef void (* TpProxyPrepareAsync) (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data); + +struct _TpProxyFeature { + /*<public>*/ + GQuark name; + gboolean core; + + TpProxyPrepareAsync prepare_async; + TpProxyPrepareAsync prepare_before_signalling_connected_async; + + const GQuark *interfaces_needed; + /* Features we depend on */ + const GQuark *depends_on; + + gboolean can_retry; + + /*<private>*/ + GCallback _reserved[4]; + TpProxyFeaturePrivate *priv; +}; /* XXX: hide this from the g-i scanner, since vapigen can't cope */ #ifndef __GI_SCANNER__ diff --git a/telepathy-glib/text-channel.c b/telepathy-glib/text-channel.c index dec83cdd..5cf42421 100644 --- a/telepathy-glib/text-channel.c +++ b/telepathy-glib/text-channel.c @@ -581,6 +581,17 @@ got_pending_senders_contact (TpTextChannel *self, } static void +free_parts_list (GList *parts_list) +{ + GList *l; + + for (l = parts_list; l != NULL; l = g_list_next (l)) + g_boxed_free (TP_ARRAY_TYPE_MESSAGE_PART_LIST, l->data); + + g_list_free (parts_list); +} + +static void got_pending_senders_contact_by_handle_cb (TpConnection *connection, guint n_contacts, TpContact * const *contacts, @@ -590,8 +601,10 @@ got_pending_senders_contact_by_handle_cb (TpConnection *connection, gpointer user_data, GObject *weak_object) { - TpTextChannel *self = (TpTextChannel *) weak_object; + GSimpleAsyncResult *result = (GSimpleAsyncResult *) weak_object; GList *parts_list = user_data; + TpTextChannel *self = TP_TEXT_CHANNEL (g_async_result_get_source_object ( + G_ASYNC_RESULT (result))); if (error != NULL) { @@ -621,8 +634,10 @@ got_pending_senders_contact_by_id_cb (TpConnection *connection, gpointer user_data, GObject *weak_object) { - TpTextChannel *self = (TpTextChannel *) weak_object; + GSimpleAsyncResult *result = (GSimpleAsyncResult *) weak_object; GList *parts_list = user_data; + TpTextChannel *self = TP_TEXT_CHANNEL (g_async_result_get_source_object ( + G_ASYNC_RESULT (result))); if (error != NULL) { @@ -650,18 +665,6 @@ out: TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, TRUE); } -static void -free_parts_list (gpointer data) -{ - GList *parts_list = data; - GList *l; - - for (l = parts_list; l != NULL; l = g_list_next (l)) - g_boxed_free (TP_ARRAY_TYPE_MESSAGE_PART_LIST, l->data); - - g_list_free (parts_list); -} - /* There is no TP_ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST (fdo #32433) */ #define ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST dbus_g_type_get_collection (\ "GPtrArray", TP_ARRAY_TYPE_MESSAGE_PART_LIST) @@ -673,7 +676,8 @@ get_pending_messages_cb (TpProxy *proxy, gpointer user_data, GObject *weak_object) { - TpTextChannel *self = user_data; + TpTextChannel *self = (TpTextChannel *) proxy; + GSimpleAsyncResult *result = user_data; guint i; GPtrArray *messages; TpIntSet *senders; @@ -686,18 +690,20 @@ get_pending_messages_cb (TpProxy *proxy, { DEBUG ("Failed to get PendingMessages property: %s", error->message); - _tp_proxy_set_feature_prepared (proxy, - TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, FALSE); - return; + g_simple_async_result_set_error (result, error->domain, error->code, + "Failed to get PendingMessages property: %s", error->message); + + g_simple_async_result_complete (result); } if (!G_VALUE_HOLDS (value, ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST)) { DEBUG ("PendingMessages property is of the wrong type"); - _tp_proxy_set_feature_prepared (proxy, - TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, FALSE); - return; + g_simple_async_result_set_error (result, TP_ERRORS, TP_ERROR_CONFUSED, + "PendingMessages property is of the wrong type"); + + g_simple_async_result_complete (result); } senders = tp_intset_new (); @@ -739,8 +745,7 @@ get_pending_messages_cb (TpProxy *proxy, if (tp_intset_size (senders) == 0) { - _tp_proxy_set_feature_prepared (proxy, - TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, TRUE); + g_simple_async_result_complete (result); } else { @@ -753,13 +758,14 @@ get_pending_messages_cb (TpProxy *proxy, DEBUG ("Pending messages may be re-ordered, please fix CM (%s)", tp_proxy_get_object_path (conn)); + /* Pass ownership of parts_list to the callback */ if (sender_ids->len == g_list_length (parts_list)) { /* Use the sender ID rather than the handles */ tp_connection_get_contacts_by_id (conn, sender_ids->len, (const gchar * const *) sender_ids->pdata, 0, NULL, got_pending_senders_contact_by_id_cb, parts_list, - free_parts_list, G_OBJECT (self)); + (GDestroyNotify) free_parts_list, G_OBJECT (result)); } else { @@ -768,7 +774,7 @@ get_pending_messages_cb (TpProxy *proxy, tp_connection_get_contacts_by_handle (conn, tmp->len, (TpHandle *) tmp->data, 0, NULL, got_pending_senders_contact_by_handle_cb, parts_list, - free_parts_list, G_OBJECT (self)); + (GDestroyNotify) free_parts_list, G_OBJECT (result)); g_array_unref (tmp); } @@ -779,10 +785,17 @@ get_pending_messages_cb (TpProxy *proxy, } static void -tp_text_channel_prepare_pending_messages (TpProxy *proxy) +tp_text_channel_prepare_pending_messages_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) { TpChannel *channel = (TpChannel *) proxy; GError *error = NULL; + GSimpleAsyncResult *result; + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + tp_text_channel_prepare_pending_messages_async); tp_cli_channel_interface_messages_connect_to_message_received (channel, message_received_cb, proxy, NULL, G_OBJECT (proxy), &error); @@ -804,15 +817,15 @@ tp_text_channel_prepare_pending_messages (TpProxy *proxy) tp_cli_dbus_properties_call_get (proxy, -1, TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "PendingMessages", - get_pending_messages_cb, proxy, NULL, G_OBJECT (proxy)); + get_pending_messages_cb, result, g_object_unref, G_OBJECT (proxy)); return; fail: - g_error_free (error); + g_simple_async_result_take_error (result, error); - _tp_proxy_set_feature_prepared (proxy, - TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES, FALSE); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); } enum { @@ -830,8 +843,8 @@ tp_text_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_PENDING_MESSAGES].name = TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES; - features[FEAT_PENDING_MESSAGES].start_preparing = - tp_text_channel_prepare_pending_messages; + features[FEAT_PENDING_MESSAGES].prepare_async = + tp_text_channel_prepare_pending_messages_async; /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am index db9d0b9c..cf0d3e33 100644 --- a/tests/dbus/Makefile.am +++ b/tests/dbus/Makefile.am @@ -43,6 +43,7 @@ noinst_PROGRAMS = \ test-params-cm \ test-properties \ test-protocol-objects \ + test-proxy-preparation \ test-self-handle \ test-self-presence \ test-simple-approver \ @@ -190,9 +191,9 @@ test_simple_handler_SOURCES = simple-handler.c test_stream_tube_SOURCES = stream-tube.c - test_client_channel_factory_SOURCES = client-channel-factory.c +test_proxy_preparation_SOURCES = proxy-preparation.c # this one uses internal ABI test_cm_message_SOURCES = \ diff --git a/tests/dbus/account-channel-request.c b/tests/dbus/account-channel-request.c index 20e05ecc..7d673d9c 100644 --- a/tests/dbus/account-channel-request.c +++ b/tests/dbus/account-channel-request.c @@ -955,11 +955,7 @@ int main (int argc, char **argv) { - g_type_init (); - tp_tests_abort_after (10); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); /* Request and handle tests */ diff --git a/tests/dbus/base-client.c b/tests/dbus/base-client.c index 7a8fb047..6e3c2581 100644 --- a/tests/dbus/base-client.c +++ b/tests/dbus/base-client.c @@ -441,7 +441,7 @@ no_return_cb (TpClient *proxy, out: test->wait--; - if (test->wait <= 0) + if (test->wait == 0) g_main_loop_quit (test->mainloop); } @@ -534,6 +534,7 @@ test_observer (Test *test, channels, "/", requests_satisified, info, no_return_cb, test, NULL, NULL); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -552,6 +553,7 @@ test_observer (Test *test, channels, "/", requests_satisified, info, no_return_cb, test, NULL, NULL); + test->wait++; g_main_loop_run (test->mainloop); g_assert_error (test->error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT); g_clear_error (&test->error); @@ -567,6 +569,7 @@ test_observer (Test *test, tp_tests_text_channel_null_close (test->text_chan_service); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -675,6 +678,7 @@ test_approver (Test *test, channels, CDO_PATH, properties, no_return_cb, test, NULL, NULL); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -709,6 +713,7 @@ test_approver (Test *test, tp_tests_text_channel_null_close (test->text_chan_service_2); + test->wait++; g_object_unref (test->text_chan_service_2); test->text_chan_service_2 = NULL; @@ -740,6 +745,7 @@ test_approver (Test *test, tp_tests_simple_channel_dispatch_operation_lost_channel (test->cdo_service, test->text_chan); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -880,6 +886,7 @@ test_handler (Test *test, channels, requests_satisified, 0, info, no_return_cb, test, NULL, NULL); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -1076,6 +1083,7 @@ test_handler_requests (Test *test, channels, requests_satisified, 0, info, no_return_cb, test, NULL, NULL); + test->wait++; g_main_loop_run (test->mainloop); g_assert_no_error (test->error); @@ -1109,15 +1117,109 @@ test_handler_requests (Test *test, g_hash_table_unref (info); } +static void +claim_with_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_channel_dispatch_operation_claim_with_finish ( + TP_CHANNEL_DISPATCH_OPERATION (source), result, &test->error); + + test->wait--; + if (test->wait == 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_channel_dispatch_operation_claim_with_async (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GPtrArray *channels; + GHashTable *properties; + static const char *interfaces[] = { NULL }; + static const gchar *possible_handlers[] = { + TP_CLIENT_BUS_NAME_BASE ".Badger", NULL, }; + TpChannelDispatchOperation *cdo; + GList *handled; + + /* Register an Approver and Handler */ + tp_base_client_take_approver_filter (test->base_client, tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_TEXT, + NULL)); + + tp_base_client_take_handler_filter (test->base_client, tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_TEXT, + NULL)); + + tp_base_client_register (test->base_client, &test->error); + g_assert_no_error (test->error); + + /* Call AddDispatchOperation */ + channels = g_ptr_array_sized_new (2); + add_channel_to_ptr_array (channels, test->text_chan); + add_channel_to_ptr_array (channels, test->text_chan_2); + + properties = tp_asv_new ( + TP_PROP_CHANNEL_DISPATCH_OPERATION_INTERFACES, + G_TYPE_STRV, interfaces, + TP_PROP_CHANNEL_DISPATCH_OPERATION_CONNECTION, + DBUS_TYPE_G_OBJECT_PATH, tp_proxy_get_object_path (test->connection), + TP_PROP_CHANNEL_DISPATCH_OPERATION_ACCOUNT, + DBUS_TYPE_G_OBJECT_PATH, tp_proxy_get_object_path (test->account), + TP_PROP_CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS, + G_TYPE_STRV, possible_handlers, + NULL); + + tp_proxy_add_interface_by_id (TP_PROXY (test->client), + TP_IFACE_QUARK_CLIENT_APPROVER); + + tp_cli_client_approver_call_add_dispatch_operation (test->client, -1, + channels, CDO_PATH, properties, + no_return_cb, test, NULL, NULL); + + test->wait++; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + cdo = test->simple_client->add_dispatch_ctx->dispatch_operation; + g_assert (TP_IS_CHANNEL_DISPATCH_OPERATION (cdo)); + + handled = tp_base_client_get_handled_channels (test->base_client); + g_assert (handled == NULL); + + /* Claim the CDO, as the client is also a Handler, it is now handling the + * channels */ + tp_channel_dispatch_operation_claim_with_async (cdo, test->base_client, + claim_with_cb, test); + + test->wait++; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + handled = tp_base_client_get_handled_channels (test->base_client); + g_assert_cmpuint (g_list_length (handled), ==, 2); + g_list_free (handled); + + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan)); + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan_2)); + + g_ptr_array_foreach (channels, free_channel_details, NULL); + g_ptr_array_free (channels, TRUE); + g_hash_table_unref (properties); +} + int main (int argc, char **argv) { - g_type_init (); - tp_tests_abort_after (10); - tp_debug_set_flags ("all"); + tp_tests_init (&argc, &argv); - g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/base-client/basics", Test, NULL, setup, test_basics, teardown); @@ -1131,6 +1233,8 @@ main (int argc, teardown); g_test_add ("/base-client/handler-requests", Test, NULL, setup, test_handler_requests, teardown); + g_test_add ("/cdo/claim_with", Test, NULL, setup, + test_channel_dispatch_operation_claim_with_async, teardown); return g_test_run (); } diff --git a/tests/dbus/call-example.c b/tests/dbus/call-example.c index ccbe981a..0e3cfb7a 100644 --- a/tests/dbus/call-example.c +++ b/tests/dbus/call-example.c @@ -1085,12 +1085,10 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_set_prgname ("call-example"); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + g_set_prgname ("call-example"); - g_type_init (); future_cli_init (); g_test_add ("/call/basics", Test, NULL, setup, test_basics, teardown); diff --git a/tests/dbus/callable-example.c b/tests/dbus/callable-example.c index 8ee33618..b3703f5f 100644 --- a/tests/dbus/callable-example.c +++ b/tests/dbus/callable-example.c @@ -1548,8 +1548,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/callable/basics", Test, NULL, setup, test_basics, teardown); diff --git a/tests/dbus/channel-dispatch-operation.c b/tests/dbus/channel-dispatch-operation.c index 0e6ed82e..4f7f9e27 100644 --- a/tests/dbus/channel-dispatch-operation.c +++ b/tests/dbus/channel-dispatch-operation.c @@ -655,6 +655,9 @@ test_claim (Test *test, g_main_loop_run (test->mainloop); g_assert_no_error (test->error); + + /* tp_channel_dispatch_operation_claim_with_async() is tested in + * tests/dbus/base-client.c */ } static void @@ -780,8 +783,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/cdo/new", Test, NULL, setup, test_new, teardown); diff --git a/tests/dbus/channel-dispatcher.c b/tests/dbus/channel-dispatcher.c index 3e60e901..e84ba805 100644 --- a/tests/dbus/channel-dispatcher.c +++ b/tests/dbus/channel-dispatcher.c @@ -65,8 +65,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/cd/new", Test, NULL, setup, test_new, teardown); diff --git a/tests/dbus/channel-request.c b/tests/dbus/channel-request.c index ad3e2ff6..7aba2e29 100644 --- a/tests/dbus/channel-request.c +++ b/tests/dbus/channel-request.c @@ -368,8 +368,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/cr/new", Test, NULL, setup, test_new, teardown); diff --git a/tests/dbus/channel.c b/tests/dbus/channel.c index d7b2932a..fab026ae 100644 --- a/tests/dbus/channel.c +++ b/tests/dbus/channel.c @@ -416,11 +416,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/channel/leave/contact/unprepared/no-reason", Test, NULL, setup, diff --git a/tests/dbus/client-channel-factory.c b/tests/dbus/client-channel-factory.c index fecf98a6..e68ec0cc 100644 --- a/tests/dbus/client-channel-factory.c +++ b/tests/dbus/client-channel-factory.c @@ -250,11 +250,7 @@ int main (int argc, char **argv) { - g_type_init (); - tp_tests_abort_after (10); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/client-channel-factory/basic/creation", Test, NULL, setup, diff --git a/tests/dbus/client.c b/tests/dbus/client.c index e06a3d26..4b88ddae 100644 --- a/tests/dbus/client.c +++ b/tests/dbus/client.c @@ -66,8 +66,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/client/new", Test, NULL, setup, test_new, teardown); diff --git a/tests/dbus/cm.c b/tests/dbus/cm.c index 60e67570..ca0d1ad9 100644 --- a/tests/dbus/cm.c +++ b/tests/dbus/cm.c @@ -1017,8 +1017,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add_func ("/cm/valid-name", test_valid_name); diff --git a/tests/dbus/connection-interests.c b/tests/dbus/connection-interests.c index 7655f39b..75d65ae3 100644 --- a/tests/dbus/connection-interests.c +++ b/tests/dbus/connection-interests.c @@ -392,8 +392,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (5); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_add ("/conn/interest", Test, NULL, setup, test_interest, teardown); g_test_add ("/conn/interested-client", Test, NULL, setup, diff --git a/tests/dbus/connection.c b/tests/dbus/connection.c index 95584161..b4dc04e9 100644 --- a/tests/dbus/connection.c +++ b/tests/dbus/connection.c @@ -399,8 +399,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_add ("/conn/prepare", Test, NULL, setup, test_prepare, teardown); g_test_add ("/conn/fail_to_prepare", Test, NULL, setup, test_fail_to_prepare, diff --git a/tests/dbus/contact-lists.c b/tests/dbus/contact-lists.c index b741e88e..61fa2232 100644 --- a/tests/dbus/contact-lists.c +++ b/tests/dbus/contact-lists.c @@ -2319,8 +2319,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (60); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_add ("/contact-lists/nothing", Test, NULL, setup, test_nothing, teardown); diff --git a/tests/dbus/contacts-slow-path.c b/tests/dbus/contacts-slow-path.c index 18cdb459..bb291ec3 100644 --- a/tests/dbus/contacts-slow-path.c +++ b/tests/dbus/contacts-slow-path.c @@ -1299,11 +1299,7 @@ int main (int argc, char **argv) { - g_type_init (); - tp_debug_set_flags ("all"); - tp_tests_abort_after (10); - g_set_prgname ("contacts-slow-path"); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/contacts-slow-path/by-handle", Fixture, NULL, setup, diff --git a/tests/dbus/contacts.c b/tests/dbus/contacts.c index ba2bc73c..54c6ad38 100644 --- a/tests/dbus/contacts.c +++ b/tests/dbus/contacts.c @@ -1977,20 +1977,16 @@ test_prepare_contact_caps_without_request (Fixture *f, for (i = 0; i < 3; i++) { TpCapabilities *caps; - GPtrArray *classes; g_assert_cmpuint (tp_contact_get_handle (contacts[i]), ==, handles[i]); g_assert_cmpstr (tp_contact_get_identifier (contacts[i]), ==, ids[i]); - MYASSERT (tp_contact_has_feature (contacts[i], + MYASSERT (!tp_contact_has_feature (contacts[i], TP_CONTACT_FEATURE_CAPABILITIES), ""); caps = tp_contact_get_capabilities (contacts[i]); - MYASSERT (caps != NULL, ""); - MYASSERT (!tp_capabilities_is_specific_to_contact (caps), ""); - classes = tp_capabilities_get_channel_classes (caps); - g_assert_cmpuint (classes->len, ==, 0); + MYASSERT (caps == NULL, ""); } g_assert (result.error == NULL); @@ -2310,11 +2306,7 @@ int main (int argc, char **argv) { - g_type_init (); - tp_debug_set_flags ("all"); - tp_tests_abort_after (10); - g_set_prgname ("contacts"); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); #define ADD(x) \ diff --git a/tests/dbus/dbus.c b/tests/dbus/dbus.c index a347f418..da7525fb 100644 --- a/tests/dbus/dbus.c +++ b/tests/dbus/dbus.c @@ -332,11 +332,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - tp_debug_set_flags ("all"); - - g_type_init (); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_add_func ("/dbus/validation", test_validation); g_test_add_func ("/dbus-daemon/properties", test_properties); diff --git a/tests/dbus/get-interface-after-invalidate.c b/tests/dbus/get-interface-after-invalidate.c index 9027d4ac..36abe354 100644 --- a/tests/dbus/get-interface-after-invalidate.c +++ b/tests/dbus/get-interface-after-invalidate.c @@ -34,8 +34,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_add_func ("/test-get-interface-after-invalidate", test_get_interface_after_invalidate); diff --git a/tests/dbus/group-mixin.c b/tests/dbus/group-mixin.c index 558be19f..51153be6 100644 --- a/tests/dbus/group-mixin.c +++ b/tests/dbus/group-mixin.c @@ -195,8 +195,7 @@ static void details_contains_ids_for (const GHashTable *details, TpHandle *hs) { - const GValue *member_ids_v; - GHashTable *member_ids; + GHashTable *contact_ids; const gchar *id; guint n = 0; TpHandle *h; @@ -204,19 +203,20 @@ details_contains_ids_for (const GHashTable *details, if (details == NULL) return; - member_ids_v = tp_asv_lookup (details, "member-ids"); - member_ids = g_value_get_boxed (member_ids_v); + contact_ids = tp_asv_get_boxed (details, "contact-ids", + TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP); + g_assert (contact_ids != NULL); for (h = hs; *h != 0; h++) { n++; - id = g_hash_table_lookup (member_ids, GUINT_TO_POINTER (*h)); + id = g_hash_table_lookup (contact_ids, GUINT_TO_POINTER (*h)); MYASSERT (id != NULL, ": id for %u in map", *h); g_assert_cmpstr (id, ==, tp_handle_inspect (contact_repo, *h)); } - MYASSERT (g_hash_table_size (member_ids) == n, ": %u member IDs", n); + MYASSERT (g_hash_table_size (contact_ids) == n, ": %u contact IDs", n); } static void @@ -360,7 +360,7 @@ camel_removed (const GArray *added, TpHandle h; /* camel2 is the actor. camel shouldn't be in the ids, because they were * removed and the spec says that you can leave those out, and we want - * tp-glib's automatic construction of member-ids to work in the #ubuntu + * tp-glib's automatic construction of contact-ids to work in the #ubuntu * case. */ TpHandle hs[] = { camel2, 0 }; diff --git a/tests/dbus/params-cm.c b/tests/dbus/params-cm.c index 39132c89..280b853e 100644 --- a/tests/dbus/params-cm.c +++ b/tests/dbus/params-cm.c @@ -342,8 +342,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/params-cm/set-params", Test, NULL, setup, test_set_params, diff --git a/tests/dbus/protocol-objects.c b/tests/dbus/protocol-objects.c index ecea32f3..5e499a04 100644 --- a/tests/dbus/protocol-objects.c +++ b/tests/dbus/protocol-objects.c @@ -431,8 +431,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/protocol-objects/protocol-properties", Test, NULL, setup, diff --git a/tests/dbus/proxy-preparation.c b/tests/dbus/proxy-preparation.c new file mode 100644 index 00000000..c331030a --- /dev/null +++ b/tests/dbus/proxy-preparation.c @@ -0,0 +1,399 @@ +/* Tests of TpBaseClient + * + * Copyright © 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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 <telepathy-glib/telepathy-glib.h> + +#include "tests/lib/util.h" +#include "tests/lib/simple-account.h" +#include "tests/lib/simple-conn.h" +#include "tests/lib/my-conn-proxy.h" + +typedef struct { + GMainLoop *mainloop; + TpDBusDaemon *dbus; + + /* Service side objects */ + TpBaseConnection *base_connection; + + /* Client side objects */ + TpConnection *connection; + TpTestsMyConnProxy *my_conn; + + GError *error /* initialized where needed */; + gint wait; +} Test; + +static void +setup (Test *test, + gconstpointer data) +{ + test->mainloop = g_main_loop_new (NULL, FALSE); + test->dbus = tp_tests_dbus_daemon_dup_or_die (); + + test->error = NULL; + + /* Create (service and client sides) connection objects */ + tp_tests_create_and_connect_conn (TP_TESTS_TYPE_SIMPLE_CONNECTION, + "me@test.com", &test->base_connection, &test->connection); + + test->my_conn = g_object_new (TP_TESTS_TYPE_MY_CONN_PROXY, + "dbus-daemon", test->dbus, + "bus-name", tp_proxy_get_bus_name (test->connection), + "object-path", tp_proxy_get_object_path (test->connection), + NULL); +} + +static void +disconnect_and_destroy_conn (Test *test) +{ + tp_cli_connection_run_disconnect (TP_CONNECTION (test->my_conn), -1, + &test->error, NULL); + g_assert_no_error (test->error); + + tp_clear_object (&test->connection); + tp_clear_object (&test->base_connection); + tp_clear_object (&test->my_conn); + +} + +static void +teardown (Test *test, + gconstpointer data) +{ + g_clear_error (&test->error); + + tp_clear_object (&test->dbus); + g_main_loop_unref (test->mainloop); + test->mainloop = NULL; + + disconnect_and_destroy_conn (test); +} + +static void +prepare_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Test *test = user_data; + + tp_proxy_prepare_finish (TP_PROXY (source), result, &test->error); + + test->wait--; + if (test->wait <= 0) + g_main_loop_quit (test->mainloop); +} + +static void +test_prepare_capabilities (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Prepare capabilities on a new proxy. CORE should be automatically prepared + * *before* checking if Requests is implemented as + * tp_proxy_has_interface_by_id() can't work without CORE. */ + GQuark features[] = { TP_CONNECTION_FEATURE_CAPABILITIES, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, TP_CONNECTION_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_CONNECTION_FEATURE_CAPABILITIES)); +} + +static void +test_prepare_core (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Test than preparing the 'top' core feature prepare the other core + * features as well */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_CORE, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (test->my_conn, TP_CONNECTION_FEATURE_CORE)); + + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_CONNECTION_FEATURE_CAPABILITIES)); +} + +static void +test_depends (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Test if A is automatically prepared when preparing B */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_B, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_A)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_B)); +} + +static void +test_wrong_iface (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Feature can't be prepared because proxy doesn't support the right + * interface */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE)); +} + +static void +test_bad_dep (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Feature can't be prepared because it depends on an unpreparable + * feature */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP)); +} + +static void +test_fail (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Feature preparation fails */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL)); +} + +static void +test_fail_dep (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* Feature can't be prepared because its deps can't be prepared */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP)); +} + +static void +test_retry (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* We have the prepare the feature twice */ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY, 0 }; + + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY)); + + /* second attempt */ + test->my_conn->retry_feature_success = TRUE; + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY)); +} + +static void +test_retry_dep (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + /* RETRY_DEP depends on a feature having can_retry and which failed, so + * preparing RETRY_DEP will re-prepare it successfully */ + GQuark features_retry[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY, 0 }; + GQuark features_retry_dep[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP, 0 }; + + /* Try preparing TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY, will fail */ + tp_proxy_prepare_async (test->my_conn, features_retry, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY)); + g_assert (!tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP)); + + /* Try prepare TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP, will re-prepare + * TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY */ + test->my_conn->retry_feature_success = TRUE; + tp_proxy_prepare_async (test->my_conn, features_retry_dep, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY)); + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP)); +} + +static void +recreate_connection (Test *test) +{ + gchar *name; + gchar *conn_path; + + disconnect_and_destroy_conn (test); + + test->base_connection = tp_tests_object_new_static_class ( + TP_TESTS_TYPE_SIMPLE_CONNECTION, + "account", "me@test.com", + "protocol", "simple", + NULL); + g_assert (test->base_connection != NULL); + + g_assert (tp_base_connection_register (test->base_connection, "simple", + &name, &conn_path, &test->error)); + g_assert_no_error (test->error); + + test->connection = tp_connection_new (test->dbus, name, conn_path, + &test->error); + g_assert_no_error (test->error); + + test->my_conn = g_object_new (TP_TESTS_TYPE_MY_CONN_PROXY, + "dbus-daemon", test->dbus, + "bus-name", tp_proxy_get_bus_name (test->connection), + "object-path", tp_proxy_get_object_path (test->connection), + NULL); + g_assert (test->my_conn != NULL); + + g_free (name); + g_free (conn_path); +} + +static void +test_before_connected (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED, 0 }; + GQuark connected[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; + + /* We need a not yet connected connection */ + recreate_connection (test); + + g_assert_cmpuint (test->my_conn->before_connected_state, ==, + BEFORE_CONNECTED_STATE_UNPREPARED); + + /* Connection is not yet connected, prepare the feature */ + tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + g_assert (tp_proxy_is_prepared (test->my_conn, + TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED)); + + g_assert_cmpuint (test->my_conn->before_connected_state, ==, + BEFORE_CONNECTED_STATE_NOT_CONNECTED); + + tp_cli_connection_call_connect (test->connection, -1, NULL, NULL, NULL, NULL); + + /* Wait that CONNECTED is announced */ + tp_proxy_prepare_async (test->my_conn, connected, prepare_cb, test); + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + /* state has been updated */ + g_assert_cmpuint (test->my_conn->before_connected_state, ==, + BEFORE_CONNECTED_STATE_CONNECTED); +} + +int +main (int argc, + char **argv) +{ + tp_tests_init (&argc, &argv); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/proxy-preparation/prepare-capabilities", Test, NULL, setup, + test_prepare_capabilities, teardown); + g_test_add ("/proxy-preparation/prepare-core", Test, NULL, setup, + test_prepare_core, teardown); + g_test_add ("/proxy-preparation/depends", Test, NULL, setup, + test_depends, teardown); + g_test_add ("/proxy-preparation/wrong-iface", Test, NULL, setup, + test_wrong_iface, teardown); + g_test_add ("/proxy-preparation/bad-dep", Test, NULL, setup, + test_bad_dep, teardown); + g_test_add ("/proxy-preparation/fail", Test, NULL, setup, + test_fail, teardown); + g_test_add ("/proxy-preparation/fail-dep", Test, NULL, setup, + test_fail_dep, teardown); + g_test_add ("/proxy-preparation/retry", Test, NULL, setup, + test_retry, teardown); + g_test_add ("/proxy-preparation/retry-dep", Test, NULL, setup, + test_retry_dep, teardown); + g_test_add ("/proxy-preparation/before-connected", Test, NULL, setup, + test_before_connected, teardown); + + return g_test_run (); +} diff --git a/tests/dbus/self-handle.c b/tests/dbus/self-handle.c index ccb7cdc3..ef55e095 100644 --- a/tests/dbus/self-handle.c +++ b/tests/dbus/self-handle.c @@ -350,11 +350,8 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); + tp_tests_init (&argc, &argv); g_set_prgname ("self-handle"); - g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/self-handle", Fixture, NULL, setup_and_connect, diff --git a/tests/dbus/simple-approver.c b/tests/dbus/simple-approver.c index a1865148..1cd725e1 100644 --- a/tests/dbus/simple-approver.c +++ b/tests/dbus/simple-approver.c @@ -487,11 +487,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/simple-/properties", Test, NULL, setup, test_properties, diff --git a/tests/dbus/simple-handler.c b/tests/dbus/simple-handler.c index 461667cb..211c3e51 100644 --- a/tests/dbus/simple-handler.c +++ b/tests/dbus/simple-handler.c @@ -503,11 +503,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/simple-handler/properties", Test, NULL, setup, test_properties, diff --git a/tests/dbus/simple-observer.c b/tests/dbus/simple-observer.c index eac24e4f..1d0abb29 100644 --- a/tests/dbus/simple-observer.c +++ b/tests/dbus/simple-observer.c @@ -469,11 +469,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/simple-observer/properties", Test, NULL, setup, test_properties, diff --git a/tests/dbus/stream-tube.c b/tests/dbus/stream-tube.c index 534bdc42..57c2dcf4 100644 --- a/tests/dbus/stream-tube.c +++ b/tests/dbus/stream-tube.c @@ -1014,11 +1014,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); have_ipv6 = check_ipv6_support (); diff --git a/tests/dbus/text-channel.c b/tests/dbus/text-channel.c index 880de6bc..88f6e750 100644 --- a/tests/dbus/text-channel.c +++ b/tests/dbus/text-channel.c @@ -604,11 +604,7 @@ int main (int argc, char **argv) { - tp_tests_abort_after (10); - g_type_init (); - tp_debug_set_flags ("all"); - - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/text-channel/creation", Test, NULL, setup, diff --git a/tests/dtmf-player.c b/tests/dtmf-player.c index 4574c146..3ed49faf 100644 --- a/tests/dtmf-player.c +++ b/tests/dtmf-player.c @@ -302,8 +302,7 @@ main (int argc, #define FIXTURE_TEST(x) \ g_test_add (TEST_PREFIX #x, Fixture, NULL, setup, test_ ## x, teardown) - tp_tests_abort_after (10); - g_test_init (&argc, &argv, NULL); + tp_tests_init (&argc, &argv); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add_func (TEST_PREFIX "to_char", test_to_char); diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 803d8d39..057641b0 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -19,6 +19,8 @@ libtp_glib_tests_la_SOURCES = \ echo-im-manager.h \ echo-im-manager.c \ myassert.h \ + my-conn-proxy.h \ + my-conn-proxy.c \ params-cm.h \ params-cm.c \ simple-account.c \ diff --git a/tests/lib/my-conn-proxy.c b/tests/lib/my-conn-proxy.c new file mode 100644 index 00000000..e49cd27b --- /dev/null +++ b/tests/lib/my-conn-proxy.c @@ -0,0 +1,333 @@ +/* + * my-conn-proxy.c - a simple subclass of TpConnection + * + * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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 "my-conn-proxy.h" + +#include <telepathy-glib/proxy-internal.h> + +G_DEFINE_TYPE (TpTestsMyConnProxy, tp_tests_my_conn_proxy, + TP_TYPE_CONNECTION) + +static void +tp_tests_my_conn_proxy_init (TpTestsMyConnProxy *self) +{ +} + +enum { + FEAT_CORE, + FEAT_A, + FEAT_B, + FEAT_WRONG_IFACE, + FEAT_BAD_DEP, + FEAT_FAIL, + FEAT_FAIL_DEP, + FEAT_RETRY, + FEAT_RETRY_DEP, + FEAT_BEFORE_CONNECTED, + N_FEAT +}; + +static void +prepare_core_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + /* superclass core features are prepared first */ + g_assert (tp_proxy_is_prepared (proxy, TP_CONNECTION_FEATURE_CORE)); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_core_async); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_a_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_a_async); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_b_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_A)); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_b_async); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +cannot_be_prepared_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +prepare_fail_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + + result = g_simple_async_result_new_error ((GObject *) proxy, callback, + user_data, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, + "No feature for you!"); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_retry_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy; + GSimpleAsyncResult *result; + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_retry_async); + + if (!self->retry_feature_success) + { + /* Fail the first time we try to prepare the feature */ + g_simple_async_result_set_error (result, TP_ERRORS, + TP_ERROR_NOT_YET, "Nah"); + } + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_retry_dep_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_retry_dep_async); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_before_connected_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy; + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_before_connected_async); + + if (tp_connection_get_status (TP_CONNECTION (self), NULL) == + TP_CONNECTION_STATUS_CONNECTED) + self->before_connected_state = BEFORE_CONNECTED_STATE_CONNECTED; + else + self->before_connected_state = BEFORE_CONNECTED_STATE_NOT_CONNECTED; + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +prepare_before_connected_before_async (TpProxy *proxy, + const TpProxyFeature *feature, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy; + GSimpleAsyncResult *result; + + g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE)); + + g_assert_cmpuint (tp_connection_get_status (TP_CONNECTION (proxy), NULL), ==, + TP_CONNECTION_STATUS_CONNECTING); + + result = g_simple_async_result_new ((GObject *) proxy, callback, user_data, + prepare_before_connected_before_async); + + self->before_connected_state = BEFORE_CONNECTED_STATE_CONNECTED; + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static const TpProxyFeature * +list_features (TpProxyClass *cls G_GNUC_UNUSED) +{ + static TpProxyFeature features[N_FEAT + 1] = { { 0 } }; + static GQuark need_a[2] = {0, 0}; + static GQuark need_channel_core[2] = {0, 0}; + static GQuark need_wrong_iface[2] = {0, 0}; + static GQuark need_fail[2] = {0, 0}; + static GQuark need_retry[2] = {0, 0}; + + if (G_LIKELY (features[0].name != 0)) + return features; + + features[FEAT_CORE].name = TP_TESTS_MY_CONN_PROXY_FEATURE_CORE; + features[FEAT_CORE].core = TRUE; + features[FEAT_CORE].prepare_async = prepare_core_async; + + features[FEAT_A].name = TP_TESTS_MY_CONN_PROXY_FEATURE_A; + features[FEAT_A].prepare_async = prepare_a_async; + + features[FEAT_B].name = TP_TESTS_MY_CONN_PROXY_FEATURE_B; + features[FEAT_B].prepare_async = prepare_b_async; + need_a[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_A; + features[FEAT_B].depends_on = need_a; + + features[FEAT_WRONG_IFACE].name = TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE; + features[FEAT_WRONG_IFACE].prepare_async = cannot_be_prepared_async; + need_channel_core[0] = TP_CHANNEL_FEATURE_CORE; + features[FEAT_WRONG_IFACE].interfaces_needed = need_channel_core; + + features[FEAT_BAD_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP; + features[FEAT_BAD_DEP].prepare_async = cannot_be_prepared_async; + need_wrong_iface[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE; + features[FEAT_BAD_DEP].depends_on = need_wrong_iface; + + features[FEAT_FAIL].name = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL; + features[FEAT_FAIL].prepare_async = prepare_fail_async; + + features[FEAT_FAIL_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP; + features[FEAT_FAIL_DEP].prepare_async = cannot_be_prepared_async; + need_fail[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL; + features[FEAT_FAIL_DEP].depends_on = need_fail; + + features[FEAT_RETRY].name = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY; + features[FEAT_RETRY].prepare_async = prepare_retry_async; + features[FEAT_RETRY].can_retry = TRUE; + + features[FEAT_RETRY_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP; + need_retry[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY; + features[FEAT_RETRY_DEP].prepare_async = prepare_retry_dep_async; + features[FEAT_RETRY_DEP].depends_on = need_retry; + + features[FEAT_BEFORE_CONNECTED].name = + TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED; + features[FEAT_BEFORE_CONNECTED].prepare_async = + prepare_before_connected_async; + features[FEAT_BEFORE_CONNECTED].prepare_before_signalling_connected_async = + prepare_before_connected_before_async; + + return features; +} + +static void +tp_tests_my_conn_proxy_class_init (TpTestsMyConnProxyClass *klass) +{ + TpProxyClass *proxy_class = (TpProxyClass *) klass; + + proxy_class->list_features = list_features; +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-core"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_a (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-a"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_b (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-b"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_wrong_iface (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-wrong_iface"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_bad_dep (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-bad-dep"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_fail (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-fail"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_fail_dep (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-fail-dep"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_retry (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-retry"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_retry_dep (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-retry-dep"); +} + +GQuark +tp_tests_my_conn_proxy_get_feature_quark_before_connected (void) +{ + return g_quark_from_static_string ("tp-my-conn-proxy-feature-before-connected"); +} diff --git a/tests/lib/my-conn-proxy.h b/tests/lib/my-conn-proxy.h new file mode 100644 index 00000000..cfc82160 --- /dev/null +++ b/tests/lib/my-conn-proxy.h @@ -0,0 +1,115 @@ +/* + * my-conn-proxy.h - header for a simple subclass of TpConnection + * + * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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_MY_CONN_PROXY_H__ +#define __TP_TESTS_MY_CONN_PROXY_H__ + +#include <glib-object.h> +#include <telepathy-glib/telepathy-glib.h> + + +G_BEGIN_DECLS + +typedef struct _TpTestsMyConnProxy TpTestsMyConnProxy; +typedef struct _TpTestsMyConnProxyClass TpTestsMyConnProxyClass; +typedef struct _TpTestsMyConnProxyPrivate TpTestsMyConnProxyPrivate; + +struct _TpTestsMyConnProxyClass { + TpConnectionClass parent_class; +}; + +typedef enum +{ + BEFORE_CONNECTED_STATE_UNPREPARED = 0, + BEFORE_CONNECTED_STATE_NOT_CONNECTED, + BEFORE_CONNECTED_STATE_CONNECTED, +} TpTestsMyConnProxyBeforeConnectedState; + + +struct _TpTestsMyConnProxy { + TpConnection parent; + + gboolean retry_feature_success; + TpTestsMyConnProxyBeforeConnectedState before_connected_state; +}; + +GType tp_tests_my_conn_proxy_get_type (void); + +/* TYPE MACROS */ +#define TP_TESTS_TYPE_MY_CONN_PROXY \ + (tp_tests_my_conn_proxy_get_type ()) +#define TP_TESTS_MY_CONN_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_MY_CONN_PROXY, \ + TpTestsMyConnProxy)) +#define TP_TESTS_MY_CONN_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_MY_CONN_PROXY, \ + TpTestsMyConnProxyClass)) +#define TP_TESTS_SIMPLE_IS_MY_CONN_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_MY_CONN_PROXY)) +#define TP_TESTS_SIMPLE_IS_MY_CONN_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_MY_CONN_PROXY)) +#define TP_TESTS_MY_CONN_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_MY_CONN_PROXY, \ + TpTestsMyConnProxyClass)) + +/* Core feature */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_CORE \ + (tp_tests_my_conn_proxy_get_feature_quark_core ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_core (void) G_GNUC_CONST; + +/* No depends */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_A \ + (tp_tests_my_conn_proxy_get_feature_quark_a ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_a (void) G_GNUC_CONST; + +/* Depends on A */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_B \ + (tp_tests_my_conn_proxy_get_feature_quark_b ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_b (void) G_GNUC_CONST; + +/* Depends on an unimplemented iface */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE \ + (tp_tests_my_conn_proxy_get_feature_quark_wrong_iface ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_wrong_iface (void) G_GNUC_CONST; + +/* Depends on WRONG_IFACE */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP \ + (tp_tests_my_conn_proxy_get_feature_quark_bad_dep ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_bad_dep (void) G_GNUC_CONST; + +/* Fail during preparation */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL \ + (tp_tests_my_conn_proxy_get_feature_quark_fail ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_fail (void) G_GNUC_CONST; + +/* Depends on FAIL */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP \ + (tp_tests_my_conn_proxy_get_feature_quark_fail_dep ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_fail_dep (void) G_GNUC_CONST; + +/* Fail at first attempt but succeed after */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY \ + (tp_tests_my_conn_proxy_get_feature_quark_retry ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_retry (void) G_GNUC_CONST; + +/* Depends on FEATURE_RETRY */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP \ + (tp_tests_my_conn_proxy_get_feature_quark_retry_dep ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_retry_dep (void) G_GNUC_CONST; + +/* Can be prepared before the connection is connected and block announcing the + * connected state */ +#define TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED \ + (tp_tests_my_conn_proxy_get_feature_quark_before_connected ()) +GQuark tp_tests_my_conn_proxy_get_feature_quark_before_connected (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* #ifndef __TP_TESTS_MY_CONN_PROXY_H__ */ diff --git a/tests/lib/util.c b/tests/lib/util.c index 27b4c1e5..fc2ecdb0 100644 --- a/tests/lib/util.c +++ b/tests/lib/util.c @@ -289,3 +289,14 @@ tp_tests_abort_after (guint sec) alarm (sec + 2); #endif } + +void +tp_tests_init (int *argc, + char ***argv) +{ + g_type_init (); + tp_tests_abort_after (10); + tp_debug_set_flags ("all"); + + g_test_init (argc, argv, NULL); +} diff --git a/tests/lib/util.h b/tests/lib/util.h index 7eab77dc..689cf91f 100644 --- a/tests/lib/util.h +++ b/tests/lib/util.h @@ -54,4 +54,7 @@ void tp_tests_result_ready_cb (GObject *object, void tp_tests_abort_after (guint sec); +void tp_tests_init (int *argc, + char ***argv); + #endif /* #ifndef __TP_TESTS_LIB_UTIL_H__ */ |