[Telepathy-commits] [telepathy-gabble/master] Update the ContactCapabilities spec in extensions/, finish the implementation of caps advertisement, write a full twisted test

Alban Crequy alban.crequy at collabora.co.uk
Fri Dec 5 09:42:34 PST 2008


---
 .../Connection_Interface_Contact_Capabilities.xml  |   61 ++---
 src/capabilities.c                                 |   17 +-
 src/capabilities.h                                 |    3 +-
 src/caps-hash.c                                    |    3 +-
 src/channel-manager.c                              |   45 +++-
 src/channel-manager.h                              |   30 ++-
 src/connection.c                                   |  158 ++++++---
 src/private-tubes-factory.c                        |  139 +++++++--
 tests/twisted/test-caps-tubes.py                   |  340 +++++++++++++++++---
 9 files changed, 629 insertions(+), 167 deletions(-)

diff --git a/extensions/Connection_Interface_Contact_Capabilities.xml b/extensions/Connection_Interface_Contact_Capabilities.xml
index 098a81e..c40142a 100644
--- a/extensions/Connection_Interface_Contact_Capabilities.xml
+++ b/extensions/Connection_Interface_Contact_Capabilities.xml
@@ -23,59 +23,44 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
 
     <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
       <p>An interface for connections where it is possible to know what channel
-        types may be created before the request is made to the connection
+        classes may be created before the request is made to the connection
         object. Each capability represents a commitment by the connection
         manager that it will ordinarily be able to create a channel when given
-        a request with the given type and handle.</p>
+        a request with the properties defined by the channel class.</p>
 
       <p>Capabilities pertain to particular contact handles, and represent
-        activities such as having a text chat or a voice call with the user.
-        The activities are represented by the D-Bus interface name of the
-        channel type for that activity, and channel properties which can be
-        requested.</p>
+        activities such as having a text chat, a voice call with the user or a
+        stream tube of a defined type.</p>
 
       <p>This interface also provides for user interfaces notifying the
         connection manager of what capabilities to advertise for the user. This
-        is done by using the AdvertiseContactCapabilities method, and deals with
-        the interface names of channel types and the channel properties values 
-        pertaining to them which are implemented by available client
-        processes.</p>
+        is done by using the SetSelfCapabilities method, and deals with
+        channel properties values pertaining to them which are implemented by
+        available client processes.</p>
 
     </tp:docstring>
 
-    <method name="AdvertiseContactCapabilities">
-      <arg direction="in" name="add" type="aa{sv}"
+    <method name="SetSelfCapabilities">
+      <arg direction="in" name="caps" type="aa{sv}"
            tp:type="String_Variant_Map[]">
         <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
-          An array of channel classes to add to the list of what the connection
-          can handle
-        </tp:docstring>
-      </arg>
-      <arg direction="in" name="remove" type="aa{sv}"
-           tp:type="String_Variant_Map[]">
-        <tp:docstring>
-          An array of channel classes to remove from the list of what the
+          An array of channel classes to replace to the list of what the
           connection can handle
         </tp:docstring>
       </arg>
-      <arg direction="out" type="aa{sv}" tp:type="String_Variant_Map[]">
-        <tp:docstring>
-          An array of channel classes describing the current capabilities.
-        </tp:docstring>
-      </arg>
       <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
-        <p>Used by user interfaces to indicate which channel types they are able
-        to handle on this connection. Because these may be provided by
-        different client processes, this method accepts channel types to add
-        and remove from the set already advertised on this connection.</p>
+        <p>Used by user interfaces to indicate which channel classes they are
+        able to handle on this connection. It replaces the previous advertised
+        channel classes by the set given as parameter.</p>
+
+        <p>If a channel class is unknown by the connection manager, it is just
+        ignored. No error are returned in this case, and other known channel
+        class are added.</p>
 
         <p>Upon a successful invocation of this method, the
         ContactCapabilitiesChanged signal will be emitted for the user's own
         handle (as returned by GetSelfHandle) by the connection manager to
-        indicate the changes that have been made.  This signal should also be
-        monitored to ensure that the set is kept accurate - for example, a
-        client may remove capabilities when it exits which are still provided
-        by another client.</p>
+        indicate the changes that have been made.</p>
       </tp:docstring>
       <tp:possible-errors>
         <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
@@ -117,13 +102,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
     </method>
 
     <signal name="ContactCapabilitiesChanged">
-      <arg name="caps" type="(a(ua{sv}as)a(ua{sv}as))" tp:type="Contact_Capability_Change">
+      <arg name="caps" type="a(ua{sv}as)" tp:type="Enhanced_Contact_Capability[]">
         <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
-          An struct containing:
-          <ul>
-            <li>the contact's removed capabilities</li>
-            <li>the contact's added capabilities</li>
-          </ul>
+          All the capabilities of the contact
         </tp:docstring>
       </arg>
       <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
@@ -143,7 +124,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
         Fixed_Properties nor to Allowed_Properties, the request will fail.
       </tp:docstring>
       <tp:member type="u" tp:type="Contact_Handle" name="Handle"/>
-      <tp:member type="a{sv}" tp:type="String_Variant_Map"
+      <tp:member type="a{sv}" tp:type="Channel_Class"
                  name="Fixed_Properties">
         <tp:docstring>
           A dictionary mapping the channel properties to their values.
diff --git a/src/capabilities.c b/src/capabilities.c
index 459e153..dc7fd51 100644
--- a/src/capabilities.c
+++ b/src/capabilities.c
@@ -20,7 +20,7 @@
 
 #include "config.h"
 #include "capabilities.h"
-
+#include "channel-manager.h"
 #include "namespaces.h"
 #include "presence-cache.h"
 #include <telepathy-glib/interfaces.h>
@@ -53,8 +53,12 @@ static const Feature self_advertised_features[] =
 };
 
 GSList *
-capabilities_get_features (GabblePresenceCapabilities caps)
+capabilities_get_features (GabblePresenceCapabilities caps,
+                           GHashTable *per_channel_factory_caps)
 {
+  GHashTableIter channel_manager_iter;
+  GabbleChannelManager *manager;
+  gpointer cap;
   GSList *features = NULL;
   const Feature *i;
 
@@ -62,6 +66,15 @@ capabilities_get_features (GabblePresenceCapabilities caps)
     if ((i->caps & caps) == i->caps)
       features = g_slist_append (features, (gpointer) i);
 
+  if (per_channel_factory_caps != NULL)
+    {
+      g_hash_table_iter_init (&channel_manager_iter, per_channel_factory_caps);
+      while (g_hash_table_iter_next (&channel_manager_iter, &manager, &cap))
+        {
+          gabble_channel_manager_get_feature_list (manager, cap, &features);
+        }
+    }
+
   return features;
 }
 
diff --git a/src/capabilities.h b/src/capabilities.h
index 20ad1be..1cb5fde 100644
--- a/src/capabilities.h
+++ b/src/capabilities.h
@@ -54,7 +54,8 @@ struct _Feature
  * Return a linked list of const Feature structs corresponding to the given
  * GabblePresenceCapabilities.
  */
-GSList *capabilities_get_features (GabblePresenceCapabilities caps);
+GSList *capabilities_get_features (GabblePresenceCapabilities caps,
+    GHashTable *per_channel_factory_caps);
 
 /*
  * capabilities_fill_cache
diff --git a/src/caps-hash.c b/src/caps-hash.c
index ff8a11c..14ea622 100644
--- a/src/caps-hash.c
+++ b/src/caps-hash.c
@@ -357,7 +357,8 @@ gchar *
 caps_hash_compute_from_self_presence (GabbleConnection *self)
 {
   GabblePresence *presence = self->self_presence;
-  GSList *features_list = capabilities_get_features (presence->caps);
+  GSList *features_list = capabilities_get_features (presence->caps,
+      presence->per_channel_factory_caps);
   GPtrArray *features = g_ptr_array_new ();
   GPtrArray *identities = g_ptr_array_new ();
   GPtrArray *dataforms = g_ptr_array_new ();
diff --git a/src/channel-manager.c b/src/channel-manager.c
index c9984c0..06a504e 100644
--- a/src/channel-manager.c
+++ b/src/channel-manager.c
@@ -363,6 +363,22 @@ void gabble_channel_manager_get_contact_capabilities (
   /* ... else assume there is not caps for this kind of channels */
 }
 
+void gabble_channel_manager_get_feature_list (
+    GabbleChannelManager *manager,
+    gpointer specific_caps,
+    GSList **features)
+{
+  GabbleChannelManagerIface *iface = GABBLE_CHANNEL_MANAGER_GET_INTERFACE (
+      manager);
+  GabbleChannelManagerGetFeatureListFunc method = iface->get_feature_list;
+
+  if (method != NULL)
+    {
+      method (manager, specific_caps, features);
+    }
+  /* ... else nothing to do */
+}
+
 gpointer gabble_channel_manager_parse_capabilities (
     GabbleChannelManager *manager,
     LmMessageNode *child)
@@ -425,22 +441,37 @@ void gabble_channel_manager_update_capabilities (
   /* ... else, do what? */
 }
 
-void gabble_channel_manager_get_capability_changes (
+gboolean gabble_channel_manager_capabilities_diff (
     GabbleChannelManager *manager,
     TpHandle handle,
     gpointer specific_old_caps,
-    gpointer specific_new_caps,
-    GPtrArray *added_array,
-    GPtrArray *removed_array)
+    gpointer specific_new_caps)
+{
+  GabbleChannelManagerIface *iface = GABBLE_CHANNEL_MANAGER_GET_INTERFACE (
+      manager);
+  GabbleChannelManagerCapsDiffFunc method = iface->caps_diff;
+
+  if (method != NULL)
+    {
+      return method (manager, handle, specific_old_caps, specific_new_caps);
+    }
+  /* ... else, nothing to do */
+  return FALSE;
+}
+
+void
+gabble_channel_manager_add_capability (GabbleChannelManager *manager,
+                                       GabbleConnection *conn,
+                                       TpHandle handle,
+                                       GHashTable *cap)
 {
   GabbleChannelManagerIface *iface = GABBLE_CHANNEL_MANAGER_GET_INTERFACE (
       manager);
-  GabbleChannelManagerGetCapChangesFunc method = iface->get_cap_changes;
+  GabbleChannelManagerAddCapFunc method = iface->add_cap;
 
   if (method != NULL)
     {
-      method (manager, handle, specific_old_caps, specific_new_caps,
-          added_array, removed_array);
+      method (manager, conn, handle, cap);
     }
   /* ... else, nothing to do */
 }
diff --git a/src/channel-manager.h b/src/channel-manager.h
index 4154ea7..d8aa3de 100644
--- a/src/channel-manager.h
+++ b/src/channel-manager.h
@@ -56,6 +56,9 @@ typedef void (*GabbleChannelManagerGetContactCapsFunc) (
     GabbleChannelManager *manager, GabbleConnection *conn, TpHandle handle,
     GPtrArray *arr);
 
+typedef void (*GabbleChannelManagerGetFeatureListFunc) (
+    GabbleChannelManager *manager, gpointer specific_caps, GSList **features);
+
 typedef gpointer (*GabbleChannelManagerParseCapsFunc) (
     GabbleChannelManager *manager, LmMessageNode *children);
 
@@ -69,15 +72,22 @@ typedef void (*GabbleChannelManagerCopyCapsFunc) (
 typedef void (*GabbleChannelManagerUpdateCapsFunc) (
     GabbleChannelManager *manager, gpointer *specific_caps_out, gpointer specific_caps_in);
 
-typedef void (*GabbleChannelManagerGetCapChangesFunc) (
+typedef gboolean (*GabbleChannelManagerCapsDiffFunc) (
     GabbleChannelManager *manager, TpHandle handle, gpointer specific_old_caps,
-    gpointer specific_new_caps, GPtrArray *added_array,
-    GPtrArray *removed_array);
+    gpointer specific_new_caps);
+
+typedef void (*GabbleChannelManagerAddCapFunc) (
+    GabbleChannelManager *manager, GabbleConnection *conn, TpHandle handle,
+    GHashTable *cap);
+
 
 void gabble_channel_manager_get_contact_capabilities (
     GabbleChannelManager *manager, GabbleConnection *conn, TpHandle handle,
     GPtrArray *arr);
 
+void gabble_channel_manager_get_feature_list (
+    GabbleChannelManager *manager, gpointer specific_caps, GSList **features);
+
 gpointer gabble_channel_manager_parse_capabilities (
     GabbleChannelManager *manager, LmMessageNode *children);
 
@@ -91,10 +101,14 @@ void gabble_channel_manager_update_capabilities (
     GabbleChannelManager *manager, gpointer specific_caps_out,
     gpointer specific_caps_in);
 
-void gabble_channel_manager_get_capability_changes (
+gboolean gabble_channel_manager_capabilities_diff (
     GabbleChannelManager *manager, TpHandle handle, gpointer specific_old_caps,
-    gpointer specific_new_caps, GPtrArray *added_array,
-    GPtrArray *removed_array);
+    gpointer specific_new_caps);
+
+void gabble_channel_manager_add_capability (
+    GabbleChannelManager *manager, GabbleConnection *conn, TpHandle handle,
+    GHashTable *cap);
+
 
 typedef void (*GabbleChannelManagerForeachChannelFunc) (
     GabbleChannelManager *manager, GabbleExportableChannelFunc func,
@@ -135,11 +149,13 @@ struct _GabbleChannelManagerIface {
     GTypeInterface parent;
 
     GabbleChannelManagerGetContactCapsFunc get_contact_caps;
+    GabbleChannelManagerGetFeatureListFunc get_feature_list;
     GabbleChannelManagerParseCapsFunc parse_caps;
     GabbleChannelManagerFreeCapsFunc free_caps;
     GabbleChannelManagerCopyCapsFunc copy_caps;
     GabbleChannelManagerUpdateCapsFunc update_caps;
-    GabbleChannelManagerGetCapChangesFunc get_cap_changes;
+    GabbleChannelManagerCapsDiffFunc caps_diff;
+    GabbleChannelManagerAddCapFunc add_cap;
 
     GabbleChannelManagerForeachChannelFunc foreach_channel;
 
diff --git a/src/connection.c b/src/connection.c
index 972284d..47f2680 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1517,7 +1517,8 @@ connection_iq_disco_cb (LmMessageHandler *handler,
   lm_message_node_set_attribute (identity, "name", PACKAGE_STRING);
   lm_message_node_set_attribute (identity, "type", "pc");
 
-  features = capabilities_get_features (self->self_presence->caps);
+  features = capabilities_get_features (self->self_presence->caps,
+      self->self_presence->per_channel_factory_caps);
 
   DEBUG ("caps now %u", self->self_presence->caps);
 
@@ -2129,22 +2130,38 @@ _emit_capabilities_changed (GabbleConnection *conn,
   g_ptr_array_free (caps_arr, TRUE);
 }
 
+/**
+ * gabble_connection_get_handle_contact_capabilities
+ *
+ * Add capabilities of handle to the given GPtrArray
+ */
+static void
+gabble_connection_get_handle_contact_capabilities (GabbleConnection *self,
+  TpHandle handle, GPtrArray *arr)
+{
+  guint i;
+
+  for (i = 0; i < self->channel_managers->len; i++)
+    {
+      GabbleChannelManager *manager = GABBLE_CHANNEL_MANAGER (
+          g_ptr_array_index (self->channel_managers, i));
+
+      gabble_channel_manager_get_contact_capabilities (manager, self, handle,
+          arr);
+    }
+}
+
+
 static void
 _emit_contact_capabilities_changed (GabbleConnection *conn,
                                     TpHandle handle,
                                     GHashTable *old_caps,
                                     GHashTable *new_caps)
 {
-  GValueArray *arr = g_value_array_new (2);
-  GPtrArray *removed_array;
-  GPtrArray *added_array;
-  GValue removed = {0, };
-  GValue added = {0, };
+  GPtrArray *ret;
+  gboolean diff = FALSE;
   guint i;
 
-  removed_array = g_ptr_array_new ();
-  added_array = g_ptr_array_new ();
-
   for (i = 0; i < conn->channel_managers->len; i++)
     {
       GabbleChannelManager *manager = GABBLE_CHANNEL_MANAGER (
@@ -2157,28 +2174,24 @@ _emit_contact_capabilities_changed (GabbleConnection *conn,
       if (new_caps != NULL)
         per_channel_factory_caps_new = g_hash_table_lookup (new_caps, manager);
 
-      gabble_channel_manager_get_capability_changes (manager, handle,
-          per_channel_factory_caps_old, per_channel_factory_caps_new,
-          added_array, removed_array);
+      if (gabble_channel_manager_capabilities_diff (manager, handle,
+          per_channel_factory_caps_old, per_channel_factory_caps_new))
+        {
+          diff = TRUE;
+          break;
+        }
     }
 
-  g_value_init (&removed, GABBLE_ARRAY_TYPE_ENHANCED_CONTACT_CAPABILITY_LIST);
-  g_value_init (&added, GABBLE_ARRAY_TYPE_ENHANCED_CONTACT_CAPABILITY_LIST);
-
-  g_value_set_boxed (&removed, removed_array);
-  g_value_set_boxed (&added, added_array);
-
-  g_value_array_append (arr, &removed);
-  g_value_array_append (arr, &added);
-
   /* Don't emit the D-Bus signal if there is no change */
-  if (added_array->len + removed_array->len > 0)
-    {
-      gabble_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed (
-          conn, arr);
-    }
+  if (! diff)
+    return;
+
+  ret = g_ptr_array_new ();
 
-  g_value_array_free (arr);
+  gabble_connection_get_handle_contact_capabilities (conn, handle, ret);
+  gabble_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed (
+      conn, ret);
+  g_ptr_array_free (ret, TRUE);
 }
 
 static void
@@ -2315,6 +2328,73 @@ gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *
   g_ptr_array_free (ret, TRUE);
 }
 
+/**
+ * gabble_connection_set_self_capabilities
+ *
+ * Implements D-Bus method SetSelfCapabilities
+ * on interface
+ * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ *         that occurred, D-Bus will throw the error only if this
+ *         function returns FALSE.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+static void
+gabble_connection_set_self_capabilities (
+    GabbleSvcConnectionInterfaceContactCapabilities *iface,
+    const GPtrArray *caps,
+    DBusGMethodInvocation *context)
+{
+  GabbleConnection *self = GABBLE_CONNECTION (iface);
+  TpBaseConnection *base = (TpBaseConnection *) self;
+  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+  guint i;
+  GabblePresence *pres = self->self_presence;
+  GHashTable *save_caps;
+  GError *error = NULL;
+
+  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+  /* reset the caps, and fill with the given parameter but keep a backup for
+   * diffing: we don't want to emit a signal if nothing has changed */
+  save_caps = pres->per_channel_factory_caps;
+  pres->per_channel_factory_caps = NULL;
+
+  for (i = 0; i < caps->len; i++)
+    {
+      GHashTable *cap_to_add = g_ptr_array_index (caps, i);
+      guint j;
+
+      for (j = 0; j < self->channel_managers->len; j++)
+        {
+          GabbleChannelManager *manager = GABBLE_CHANNEL_MANAGER (
+              g_ptr_array_index (self->channel_managers, j));
+
+          gabble_channel_manager_add_capability (manager, self,
+              base->self_handle, cap_to_add);
+        }
+    }
+
+  priv->caps_serial++;
+
+  if (!_gabble_connection_signal_own_presence (self, &error))
+    {
+      dbus_g_method_return_error (context, error);
+      return;
+    }
+
+  _emit_contact_capabilities_changed (self, base->self_handle,
+                                      save_caps,
+                                      pres->per_channel_factory_caps);
+  gabble_presence_cache_free_specific_cache (save_caps);
+
+
+  gabble_svc_connection_interface_contact_capabilities_return_from_set_self_capabilities
+      (context);
+}
+
 static const gchar *assumed_caps[] =
 {
   TP_IFACE_CHANNEL_TYPE_TEXT,
@@ -2396,28 +2476,6 @@ gabble_connection_get_handle_capabilities (GabbleConnection *self,
 }
 
 
-/**
- * gabble_connection_get_handle_contact_capabilities
- *
- * Add capabilities of handle to the given GPtrArray
- */
-static void
-gabble_connection_get_handle_contact_capabilities (GabbleConnection *self,
-  TpHandle handle, GPtrArray *arr)
-{
-  guint i;
-
-  for (i = 0; i < self->channel_managers->len; i++)
-    {
-      GabbleChannelManager *manager = GABBLE_CHANNEL_MANAGER (
-          g_ptr_array_index (self->channel_managers, i));
-
-      gabble_channel_manager_get_contact_capabilities (manager, self, handle,
-          arr);
-    }
-}
-
-
 static void
 conn_capabilities_fill_contact_attributes (GObject *obj,
   const GArray *contacts, GHashTable *attributes_hash)
@@ -3064,7 +3122,7 @@ gabble_conn_contact_caps_iface_init (gpointer g_iface, gpointer iface_data)
     gabble_svc_connection_interface_contact_capabilities_implement_##x (\
     klass, gabble_connection_##x)
   IMPLEMENT(get_contact_capabilities);
-  /* TODO: publish own caps */
+  IMPLEMENT(set_self_capabilities);
 #undef IMPLEMENT
 }
 
diff --git a/src/private-tubes-factory.c b/src/private-tubes-factory.c
index 1b9a4f5..5af202d 100644
--- a/src/private-tubes-factory.c
+++ b/src/private-tubes-factory.c
@@ -36,6 +36,7 @@
 #define DEBUG_FLAG GABBLE_DEBUG_TUBES
 
 #include "channel-manager.h"
+#include "capabilities.h"
 #include "connection.h"
 #include "debug.h"
 #include "muc-channel.h"
@@ -90,7 +91,9 @@ struct _GabblePrivateTubesFactoryPrivate
 typedef struct _TubesCapabilities TubesCapabilities;
 struct _TubesCapabilities
 {
+  /* Service -> Feature */
   GHashTable *stream_tube_caps;
+  /* ServiceName -> Feature */
   GHashTable *dbus_tube_caps;
 };
 
@@ -441,6 +444,7 @@ gabble_private_tubes_factory_get_contact_caps (GabbleChannelManager *manager,
                                                TpHandle handle,
                                                GPtrArray *arr)
 {
+  TpBaseConnection *base = (TpBaseConnection *) conn;
   TubesCapabilities *caps;
   GHashTable *stream_tube_caps;
   GHashTable *dbus_tube_caps;
@@ -451,7 +455,10 @@ gabble_private_tubes_factory_get_contact_caps (GabbleChannelManager *manager,
 
   g_assert (handle != 0);
 
-  presence = gabble_presence_cache_get (conn->presence_cache, handle);
+  if (handle == base->self_handle)
+    presence = conn->self_presence;
+  else
+    presence = gabble_presence_cache_get (conn->presence_cache, handle);
 
   if (presence == NULL)
     return;
@@ -469,7 +476,7 @@ gabble_private_tubes_factory_get_contact_caps (GabbleChannelManager *manager,
   if (stream_tube_caps != NULL)
     {
       g_hash_table_iter_init (&tube_caps_iter, stream_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           add_service_to_array (service, arr, TP_TUBE_TYPE_STREAM, handle);
         }
@@ -478,13 +485,36 @@ gabble_private_tubes_factory_get_contact_caps (GabbleChannelManager *manager,
   if (dbus_tube_caps != NULL)
     {
       g_hash_table_iter_init (&tube_caps_iter, dbus_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           add_service_to_array (service, arr, TP_TUBE_TYPE_DBUS, handle);
         }
     }
 }
 
+static void
+gabble_private_tubes_factory_get_feature_list (GabbleChannelManager *manager,
+                                               gpointer specific_caps,
+                                               GSList **features)
+{
+  TubesCapabilities *caps = specific_caps;
+  GHashTableIter iter;
+  gchar *service;
+  Feature *feat;
+
+  g_hash_table_iter_init (&iter, caps->stream_tube_caps);
+  while (g_hash_table_iter_next (&iter, &service, &feat))
+    {
+      *features = g_slist_append (*features, (gpointer) feat);
+    }
+
+  g_hash_table_iter_init (&iter, caps->dbus_tube_caps);
+  while (g_hash_table_iter_next (&iter, &service, &feat))
+    {
+      *features = g_slist_append (*features, (gpointer) feat);
+    }
+}
+
 static gpointer
 gabble_private_tubes_factory_parse_caps (
     GabbleChannelManager *manager,
@@ -595,14 +625,12 @@ gabble_private_tubes_factory_update_caps (
       caps_out->dbus_tube_caps, g_strdup, NULL);
 }
 
-static void
-gabble_private_tubes_factory_get_cap_changes (
+static gboolean
+gabble_private_tubes_factory_caps_diff (
     GabbleChannelManager *manager,
     TpHandle handle,
-    gpointer specific_old_caps, 
-    gpointer specific_new_caps,
-    GPtrArray *added_array,
-    GPtrArray *removed_array)
+    gpointer specific_old_caps,
+    gpointer specific_new_caps)
 {
   TubesCapabilities *old_caps = specific_old_caps;
   TubesCapabilities *new_caps = specific_new_caps;
@@ -613,27 +641,25 @@ gabble_private_tubes_factory_get_cap_changes (
   if (old_caps != NULL)
     {
       g_hash_table_iter_init (&tube_caps_iter, old_caps->stream_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           gpointer key, value;
           if (new_caps == NULL ||
               !g_hash_table_lookup_extended (new_caps->stream_tube_caps,
                   service, &key, &value))
             {
-              add_service_to_array (service, removed_array,
-                  TP_TUBE_TYPE_STREAM, handle);
+              return TRUE;
             }
         }
       g_hash_table_iter_init (&tube_caps_iter, old_caps->dbus_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           gpointer key, value;
           if (new_caps == NULL ||
               !g_hash_table_lookup_extended (new_caps->dbus_tube_caps,
                   service, &key, &value))
             {
-              add_service_to_array (service, removed_array,
-                  TP_TUBE_TYPE_DBUS, handle);
+              return TRUE;
             }
         }
     }
@@ -641,30 +667,97 @@ gabble_private_tubes_factory_get_cap_changes (
   if (new_caps != NULL)
     {
       g_hash_table_iter_init (&tube_caps_iter, new_caps->stream_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           gpointer key, value;
           if (old_caps == NULL ||
               !g_hash_table_lookup_extended (old_caps->stream_tube_caps,
                   service, &key, &value))
             {
-              add_service_to_array (service, added_array,
-                  TP_TUBE_TYPE_STREAM, handle);
+              return TRUE;
             }
         }
       g_hash_table_iter_init (&tube_caps_iter, new_caps->dbus_tube_caps);
-      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy)) 
+      while (g_hash_table_iter_next (&tube_caps_iter, &service, &dummy))
         {
           gpointer key, value;
           if (old_caps == NULL ||
               !g_hash_table_lookup_extended (old_caps->dbus_tube_caps,
                   service, &key, &value))
             {
-              add_service_to_array (service, added_array,
-                  TP_TUBE_TYPE_DBUS, handle);
+              return TRUE;
             }
         }
     }
+  return FALSE;
+}
+
+static void
+gabble_private_tubes_factory_add_cap (GabbleChannelManager *manager,
+                                      GabbleConnection *conn,
+                                      TpHandle handle,
+                                      GHashTable *cap)
+{
+  TpBaseConnection *base = (TpBaseConnection *) conn;
+  GabblePresence *presence;
+  TubesCapabilities *caps;
+  const gchar *channel_type;
+
+  channel_type = tp_asv_get_string (cap,
+            TP_IFACE_CHANNEL ".ChannelType");
+
+  /* this channel is not for this factory */
+  if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES) &&
+      tp_strdiff (channel_type, GABBLE_IFACE_CHANNEL_TYPE_STREAM_TUBE) &&
+      tp_strdiff (channel_type, GABBLE_IFACE_CHANNEL_TYPE_DBUS_TUBE))
+    return;
+
+  if (tp_asv_get_uint32 (cap,
+        TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT)
+    return;
+
+  if (handle == base->self_handle)
+    presence = conn->self_presence;
+  else
+    presence = gabble_presence_cache_get (conn->presence_cache, handle);
+
+  g_assert (presence != NULL);
+
+  if (presence->per_channel_factory_caps == NULL)
+    presence->per_channel_factory_caps = g_hash_table_new (NULL, NULL);
+
+  caps = g_hash_table_lookup (presence->per_channel_factory_caps, manager);
+  if (caps == NULL)
+    {
+      caps = g_new0 (TubesCapabilities, 1);
+      caps->stream_tube_caps = g_hash_table_new_full (g_str_hash, g_str_equal,
+          g_free, NULL);
+      caps->dbus_tube_caps = g_hash_table_new_full (g_str_hash, g_str_equal,
+          g_free, NULL);
+      g_hash_table_insert (presence->per_channel_factory_caps, manager, caps);
+    }
+
+  if (!tp_strdiff (channel_type, GABBLE_IFACE_CHANNEL_TYPE_STREAM_TUBE))
+    {
+      Feature *feat = g_new0 (Feature, 1);
+      gchar *service = g_strdup (tp_asv_get_string (cap,
+          GABBLE_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"));
+      feat->feature_type = FEATURE_OPTIONAL;
+      feat->ns = g_strdup_printf ("%s/stream/%s", NS_TUBES, service);
+      feat->caps = 0;
+      g_hash_table_insert (caps->stream_tube_caps, service, feat);
+    }
+  else if (!tp_strdiff (channel_type, GABBLE_IFACE_CHANNEL_TYPE_DBUS_TUBE))
+    {
+      Feature *feat = g_new0 (Feature, 1);
+      gchar *service = g_strdup (tp_asv_get_string (cap,
+          GABBLE_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName"));
+      feat->feature_type = FEATURE_OPTIONAL;
+      feat->ns = g_strdup_printf ("%s/dbus/%s", NS_TUBES, service);
+      feat->caps = 0;
+      g_hash_table_insert (caps->dbus_tube_caps, service, feat);
+    }
+  /* TODO: how to free the service? */
 }
 
 struct _ForeachData
@@ -1088,11 +1181,13 @@ channel_manager_iface_init (gpointer g_iface,
   GabbleChannelManagerIface *iface = g_iface;
 
   iface->get_contact_caps = gabble_private_tubes_factory_get_contact_caps;
+  iface->get_feature_list = gabble_private_tubes_factory_get_feature_list;
   iface->parse_caps = gabble_private_tubes_factory_parse_caps;
   iface->free_caps = gabble_private_tubes_factory_free_caps;
   iface->copy_caps = gabble_private_tubes_factory_copy_caps;
   iface->update_caps = gabble_private_tubes_factory_update_caps;
-  iface->get_cap_changes = gabble_private_tubes_factory_get_cap_changes;
+  iface->caps_diff = gabble_private_tubes_factory_caps_diff;
+  iface->add_cap = gabble_private_tubes_factory_add_cap;
 
 
   iface->foreach_channel = gabble_private_tubes_factory_foreach_channel;
diff --git a/tests/twisted/test-caps-tubes.py b/tests/twisted/test-caps-tubes.py
index ed3943b..a344247 100644
--- a/tests/twisted/test-caps-tubes.py
+++ b/tests/twisted/test-caps-tubes.py
@@ -1,9 +1,11 @@
 
 """
 Test tubes capabilities with Connection.Interface.ContactCapabilities.DRAFT
-Receive presence and caps from contacts and check that GetContactCapabilities
-works correctly and that ContactCapabilitiesChanged is correctly received.
-Also check that GetContactAttributes gives the same results.
+
+1. Receive presence and caps from contacts and check that
+GetContactCapabilities works correctly and that ContactCapabilitiesChanged is
+correctly received. Also check that GetContactAttributes gives the same
+results.
 
 - no tube cap at all
 - 1 stream tube cap
@@ -13,6 +15,20 @@ Also check that GetContactAttributes gives the same results.
 - 1 stream tube + 1 D-Bus tube caps, again, to test whether the caps cache
   works with tubes
 
+2. Test SetSelfCapabilities and test that a presence stanza is sent to the
+contacts, test that the D-Bus signal ContactCapabilitiesChanged is fired for
+the self handle, ask Gabble for its caps with an iq request, check the reply
+is correct, and ask Gabble for its caps using D-Bus method
+GetContactCapabilities. Also check that GetContactAttributes gives the same
+results.
+
+- no tube cap at all
+- 1 stream tube cap
+- 1 D-Bus tube cap
+- 1 stream tube + 1 D-Bus tube caps
+- 2 stream tube + 2 D-Bus tube caps
+- 1 stream tube + 1 D-Bus tube caps, again, just for the fun
+
 """
 
 import dbus
@@ -106,7 +122,55 @@ def presence_add_caps(presence, ver, client, hash=None):
         c['hash'] = hash
     return presence
 
-def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
+def receive_presence_and_ask_caps(q, stream):
+    # receive presence stanza
+    event_stream, event_dbus = q.expect_many(
+            EventPattern('stream-presence'),
+            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged')
+        )
+    signaled_caps = event_dbus.args[0]
+
+    c_nodes = xpath.queryForNodes('/presence/c', event_stream.stanza)
+    assert c_nodes is not None
+    assert len(c_nodes) == 1
+    hash = c_nodes[0].attributes['hash']
+    ver = c_nodes[0].attributes['ver']
+    node = c_nodes[0].attributes['node']
+    assert hash == 'sha-1'
+
+    # ask caps
+    request = """
+<iq from='fake_contact at jabber.org/resource' 
+    id='disco1'
+    to='gabble at jabber.org/resource' 
+    type='get'>
+  <query xmlns='http://jabber.org/protocol/disco#info'
+         node='""" + node + '#' + ver + """'/>
+</iq>
+"""
+    stream.send(request)
+
+    # receive caps
+    event = q.expect('stream-iq',
+        query_ns='http://jabber.org/protocol/disco#info')
+    caps_str = str(xpath.queryForNodes('/iq/query/feature', event.stanza))
+
+    return (event, caps_str, signaled_caps)
+
+def caps_contain(event, cap):
+    node = xpath.queryForNodes('/iq/query/feature[@var="%s"]'
+            % cap,
+            event.stanza)
+    if node is None:
+        return False
+    if len(node) != 1:
+        return False
+    var = node[0].attributes['var']
+    if var is None:
+        return False
+    return var == cap
+
+def test_tube_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client):
 
     conn_caps_iface = dbus.Interface(conn, caps_iface)
     conn_contacts_iface = dbus.Interface(conn, contacts_iface)
@@ -180,11 +244,9 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
     stream.send(result)
 
     event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
-    removed = event.args[0][0]
-    added = event.args[0][1]
-    assert len(removed) == 0, removed
-    assert len(added) == 1, added
-    assert added[0][1] \
+    signaled_caps = event.args[0]
+    assert len(signaled_caps) == 2, signaled_caps # basic caps + daap
+    assert signaled_caps[1][1] \
         ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
         == 'daap'
 
@@ -227,14 +289,9 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
     stream.send(result)
 
     event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
-    removed = event.args[0][0]
-    added = event.args[0][1]
-    assert len(removed) == 1, removed
-    assert len(added) == 1, added
-    assert removed[0][1] \
-        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
-        == 'daap'
-    assert added[0][1] \
+    signaled_caps = event.args[0]
+    assert len(signaled_caps) == 2, signaled_caps # basic caps + Xiangqi
+    assert signaled_caps[1][1] \
         ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
         == 'com.example.Xiangqi'
 
@@ -279,13 +336,14 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
     stream.send(result)
 
     event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
-    removed = event.args[0][0]
-    added = event.args[0][1]
-    assert len(removed) == 0, removed
-    assert len(added) == 1, added
-    assert added[0][1] \
+    signaled_caps = event.args[0]
+    assert len(signaled_caps) == 3, signaled_caps # basic caps + daap+xiangqi
+    assert signaled_caps[1][1] \
         ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
         == 'daap'
+    assert signaled_caps[2][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
 
     # daap + xiangqi capabilities
     daap_xiangqi_caps = [
@@ -333,14 +391,18 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
     stream.send(result)
 
     event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
-    removed = event.args[0][0]
-    added = event.args[0][1]
-    assert len(removed) == 0, removed
-    assert len(added) == 2, added
-    assert added[0][1] \
+    signaled_caps = event.args[0]
+    assert len(signaled_caps) == 5, signaled_caps # basic caps + 4 tubes
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'daap'
+    assert signaled_caps[2][1] \
         ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
         == 'http'
-    assert added[1][1] \
+    assert signaled_caps[3][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
+    assert signaled_caps[4][1] \
         ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
         == 'com.example.Go'
 
@@ -374,16 +436,14 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
     # Gabble does not look up our capabilities because of the cache
 
     event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
-    removed = event.args[0][0]
-    added = event.args[0][1]
-    assert len(removed) == 2, removed
-    assert len(added) == 0, added
-    assert removed[0][1] \
+    signaled_caps = event.args[0]
+    assert len(signaled_caps) == 3, signaled_caps # basic caps + daap+xiangqi
+    assert signaled_caps[1][1] \
         ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
-        == 'http'
-    assert removed[1][1] \
+        == 'daap'
+    assert signaled_caps[2][1] \
         ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
-        == 'com.example.Go'
+        == 'com.example.Xiangqi'
 
     # daap + xiangqi capabilities
     daap_xiangqi_caps = [
@@ -401,6 +461,209 @@ def _test_tube_caps(q, bus, conn, stream, contact, contact_handle, client):
             [contact_handle][caps_iface + '/caps']
     assert caps_via_contacts_iface == caps, caps_via_contacts_iface
 
+def test_tube_caps_to_contact(q, bus, conn, stream):
+    basic_caps = [(1, text_fixed_properties,
+            text_allowed_properties)]
+    daap_caps = [
+        (1, text_fixed_properties, text_allowed_properties),
+        (1, daap_fixed_properties, daap_allowed_properties)]
+    xiangqi_caps = [
+        (1, text_fixed_properties, text_allowed_properties),
+        (1, xiangqi_fixed_properties, xiangqi_allowed_properties)]
+    daap_xiangqi_caps = [
+        (1, text_fixed_properties, text_allowed_properties),
+        (1, daap_fixed_properties, daap_allowed_properties),
+        (1, xiangqi_fixed_properties, xiangqi_allowed_properties)]
+    all_tubes_caps = [
+        (1, text_fixed_properties, text_allowed_properties),
+        (1, daap_fixed_properties, daap_allowed_properties),
+        (1, http_fixed_properties, http_allowed_properties),
+        (1, xiangqi_fixed_properties, xiangqi_allowed_properties),
+        (1, go_fixed_properties, go_allowed_properties)]
+
+    conn_caps_iface = dbus.Interface(conn, caps_iface)
+    conn_contacts_iface = dbus.Interface(conn, contacts_iface)
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert caps == basic_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    # Advertise nothing
+    conn_caps_iface.SetSelfCapabilities([])
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 1
+    assert caps == basic_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    sync_stream(q, stream)
+
+    # Advertise daap
+    ret_caps = conn_caps_iface.SetSelfCapabilities(
+        [daap_fixed_properties])
+
+    # Expect Gabble to reply with the correct caps
+    event, caps_str, signaled_caps = receive_presence_and_ask_caps(q, stream)
+    assert caps_contain(event, ns_tubes) == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/daap') == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/http') == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Go') \
+            == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Xiangqi') \
+            == False, caps_str
+    assert len(signaled_caps) == 2, signaled_caps # basic caps + daap
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'daap'
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 2
+    assert caps == daap_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    # Advertise xiangqi
+    ret_caps = conn_caps_iface.SetSelfCapabilities(
+        [xiangqi_fixed_properties])
+
+    # Expect Gabble to reply with the correct caps
+    event, caps_str, signaled_caps = receive_presence_and_ask_caps(q, stream)
+    assert caps_contain(event, ns_tubes) == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/daap') == False, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/http') == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Go') \
+            == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Xiangqi') \
+            == True, caps_str
+    assert len(signaled_caps) == 2, signaled_caps # basic caps + daap
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 2
+    assert caps == xiangqi_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    # Advertise daap + xiangqi
+    ret_caps = conn_caps_iface.SetSelfCapabilities(
+        [daap_fixed_properties, xiangqi_fixed_properties])
+
+    # Expect Gabble to reply with the correct caps
+    event, caps_str, signaled_caps = receive_presence_and_ask_caps(q, stream)
+    assert caps_contain(event, ns_tubes) == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/daap') == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/http') == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Go') \
+            == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Xiangqi') \
+            == True, caps_str
+    assert len(signaled_caps) == 3, signaled_caps # basic caps + daap+xiangqi
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'daap'
+    assert signaled_caps[2][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 3
+    assert caps == daap_xiangqi_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    # Advertise 4 tubes
+    ret_caps = conn_caps_iface.SetSelfCapabilities(
+        [daap_fixed_properties, http_fixed_properties,
+         go_fixed_properties, xiangqi_fixed_properties])
+
+    # Expect Gabble to reply with the correct caps
+    event, caps_str, signaled_caps = receive_presence_and_ask_caps(q, stream)
+    assert caps_contain(event, ns_tubes) == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/daap') == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/http') == True, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Go') \
+            == True, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Xiangqi') \
+            == True, caps_str
+    assert len(signaled_caps) == 5, signaled_caps # basic caps + 4 tubes
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'daap'
+    assert signaled_caps[2][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'http'
+    assert signaled_caps[3][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
+    assert signaled_caps[4][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Go'
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 5
+    assert caps == all_tubes_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
+    # Advertise daap + xiangqi
+    ret_caps = conn_caps_iface.SetSelfCapabilities(
+        [daap_fixed_properties, xiangqi_fixed_properties])
+
+    # Expect Gabble to reply with the correct caps
+    event, caps_str, signaled_caps = receive_presence_and_ask_caps(q, stream)
+    assert caps_contain(event, ns_tubes) == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/daap') == True, caps_str
+    assert caps_contain(event, ns_tubes + '/stream/http') == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Go') \
+            == False, caps_str
+    assert caps_contain(event, ns_tubes + '/dbus/com.example.Xiangqi') \
+            == True, caps_str
+    assert len(signaled_caps) == 3, signaled_caps # basic caps + daap+xiangqi
+    assert signaled_caps[1][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT.Service'] \
+        == 'daap'
+    assert signaled_caps[2][1] \
+        ['org.freedesktop.Telepathy.Channel.Type.DBusTube.DRAFT.ServiceName'] \
+        == 'com.example.Xiangqi'
+
+    # Check our own caps
+    caps = conn_caps_iface.GetContactCapabilities([1])
+    assert len(caps) == 3
+    assert caps == daap_xiangqi_caps, caps
+    # check the Contacts interface give the same caps
+    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
+            [1], [caps_iface], False) \
+            [1][caps_iface + '/caps']
+    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
+
 
 def test(q, bus, conn, stream):
     conn.Connect()
@@ -408,7 +671,10 @@ def test(q, bus, conn, stream):
 
     client = 'http://telepathy.freedesktop.org/fake-client'
 
-    _test_tube_caps(q, bus, conn, stream, 'bilbo1 at foo.com/Foo', 2L, client)
+    test_tube_caps_from_contact(q, bus, conn, stream, 'bilbo1 at foo.com/Foo',
+        2L, client)
+
+    test_tube_caps_to_contact(q, bus, conn, stream)
 
     conn.Disconnect()
     q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
-- 
1.5.6.5




More information about the Telepathy-commits mailing list