[Telepathy-commits] [telepathy-gabble/master] implement a current-activity-cache

Guillaume Desmottes guillaume.desmottes at collabora.co.uk
Fri Sep 26 10:02:26 PDT 2008


20080702082842-7fe3f-611278a0234cc73ac7b1775686a2ce7737aa8d6c.gz
---
 src/conn-olpc.c                        |   73 +++++++++--
 src/connection.c                       |    3 +-
 src/connection.h                       |    1 +
 tests/twisted/Makefile.am              |    1 +
 tests/twisted/olpc/current-activity.py |  237 ++++++++++++++++++++++++++++++++
 5 files changed, 304 insertions(+), 11 deletions(-)
 create mode 100644 tests/twisted/olpc/current-activity.py

diff --git a/src/conn-olpc.c b/src/conn-olpc.c
index f6a0667..b744cf0 100644
--- a/src/conn-olpc.c
+++ b/src/conn-olpc.c
@@ -1456,13 +1456,16 @@ add_activity_info_in_set (GabbleConnection *conn,
 static GabbleOlpcActivity *
 extract_current_activity (GabbleConnection *conn,
                           LmMessageNode *node,
-                          const gchar *contact)
+                          const gchar *contact,
+                          gboolean create_activity)
 {
   const gchar *room, *id;
   GabbleOlpcActivity *activity;
   TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
       (TpBaseConnection *) conn, TP_HANDLE_TYPE_ROOM);
-  TpHandle room_handle;
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+      (TpBaseConnection *) conn, TP_HANDLE_TYPE_CONTACT);
+  TpHandle room_handle, contact_handle;
 
   if (node == NULL)
     return NULL;
@@ -1471,16 +1474,20 @@ extract_current_activity (GabbleConnection *conn,
 
   room = lm_message_node_get_attribute (node, "room");
   if (room == NULL || room[0] == '\0')
-    return FALSE;
+    return NULL;
 
   room_handle = tp_handle_ensure (room_repo, room, NULL, NULL);
   if (room_handle == 0)
     return NULL;
 
+  contact_handle = tp_handle_lookup (contact_repo, contact, NULL, NULL);
+  if (contact_handle == 0)
+    return NULL;
+
   activity = g_hash_table_lookup (conn->olpc_activities_info,
       GUINT_TO_POINTER (room_handle));
 
-  if (activity == NULL)
+  if (activity == NULL && create_activity)
     {
       /* Humm we received as current activity an activity we don't know yet.
        * If the remote user doesn't announce this activity
@@ -1496,6 +1503,20 @@ extract_current_activity (GabbleConnection *conn,
 
   tp_handle_unref (room_repo, room_handle);
 
+  /* update current-activity cache */
+  if (activity != NULL)
+    {
+      g_hash_table_insert (conn->olpc_current_act,
+          GUINT_TO_POINTER (contact_handle), activity);
+
+      g_object_ref (activity);
+    }
+  else
+    {
+      g_hash_table_remove (conn->olpc_current_act,
+          GUINT_TO_POINTER (contact_handle));
+    }
+
   return activity;
 }
 
@@ -1511,12 +1532,19 @@ get_current_activity_reply_cb (GabbleConnection *conn,
   const gchar *from;
   GabbleOlpcActivity *activity;
 
-  if (!check_query_reply_msg (reply_msg, context))
-    return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+  if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
+    {
+      DEBUG ("Failed to query PEP node. No current activity");
+
+      gabble_svc_olpc_buddy_info_return_from_get_current_activity (context,
+          "", 0);
+
+      return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+    }
 
   from = lm_message_node_get_attribute (reply_msg->node, "from");
   node = lm_message_node_find_child (reply_msg->node, "activity");
-  activity = extract_current_activity (conn, node, from);
+  activity = extract_current_activity (conn, node, from, TRUE);
   if (activity == NULL)
     {
       DEBUG ("GetCurrentActivity returns no activity");
@@ -1544,6 +1572,7 @@ olpc_buddy_info_get_current_activity (GabbleSvcOLPCBuddyInfo *iface,
   GabbleConnection *conn = GABBLE_CONNECTION (iface);
   TpBaseConnection *base = (TpBaseConnection *) conn;
   const gchar *jid;
+  GabbleOlpcActivity *activity;
 
   DEBUG ("called for contact#%u", contact);
 
@@ -1555,7 +1584,21 @@ olpc_buddy_info_get_current_activity (GabbleSvcOLPCBuddyInfo *iface,
   if (jid == NULL)
     return;
 
-  if (!pubsub_query (conn, jid, NS_OLPC_CURRENT_ACTIVITY,
+  activity = g_hash_table_lookup (conn->olpc_current_act,
+      GUINT_TO_POINTER (contact));
+  if (activity != NULL)
+    {
+      DEBUG ("found current activity in cache: %s (%u)", activity->id,
+          activity->room);
+
+      gabble_svc_olpc_buddy_info_return_from_get_current_activity (context,
+          activity->id, activity->room);
+      return;
+    }
+
+    DEBUG ("current activity not in cache, query PEP node");
+
+    if (!pubsub_query (conn, jid, NS_OLPC_CURRENT_ACTIVITY,
         get_current_activity_reply_cb, context))
     {
       GError error = { TP_ERRORS, TP_ERROR_NETWORK_ERROR,
@@ -1679,7 +1722,7 @@ olpc_buddy_info_current_activity_event_handler (GabbleConnection *conn,
   from = lm_message_node_get_attribute (msg->node, "from");
   node = lm_message_node_find_child (msg->node, "activity");
 
-  activity = extract_current_activity (conn, node, from);
+  activity = extract_current_activity (conn, node, from, TRUE);
   if (activity != NULL)
     {
       DEBUG ("emitting CurrentActivityChanged(contact#%u, ID \"%s\", room#%u)",
@@ -3024,7 +3067,10 @@ buddy_changed (GabbleConnection *conn,
       /* Buddy current activity change */
       GabbleOlpcActivity *activity;
 
-      activity = extract_current_activity (conn, node, jid);
+      /* extract_current_activity won't create the activity if we don't
+       * know it yet as we'll have no way to find activity's info if
+       * the activity is not in a view */
+      activity = extract_current_activity (conn, node, jid, FALSE);
       if (activity == NULL)
         {
           gabble_svc_olpc_buddy_info_emit_current_activity_changed (conn,
@@ -3637,6 +3683,13 @@ conn_olpc_activity_properties_init (GabbleConnection *conn)
   conn->olpc_views = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       NULL, (GDestroyNotify) g_object_unref);
 
+  /* Current activity
+   *
+   * contact TpHandle => reffed GabbleOlpcActivity
+   */
+  conn->olpc_current_act = g_hash_table_new_full (g_direct_hash,
+      g_direct_equal, NULL, (GDestroyNotify) g_object_unref);
+
   conn->olpc_gadget_buddy = NULL;
   conn->olpc_gadget_activity = NULL;
 
diff --git a/src/connection.c b/src/connection.c
index fb2750b..8c25ebe 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -752,10 +752,11 @@ gabble_connection_dispose (GObject *object)
   g_object_unref (self->presence_cache);
   self->presence_cache = NULL;
 
-  g_hash_table_destroy (self->olpc_activities_info);
+  g_hash_table_destroy (self->olpc_current_act);
   g_hash_table_destroy (self->olpc_pep_activities);
   g_hash_table_destroy (self->olpc_invited_activities);
   g_hash_table_destroy (self->olpc_views);
+  g_hash_table_destroy (self->olpc_activities_info);
 
   g_hash_table_destroy (self->avatar_requests);
 
diff --git a/src/connection.h b/src/connection.h
index 41c6ea5..07a7dfa 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -141,6 +141,7 @@ struct _GabbleConnection {
     GHashTable *olpc_pep_activities;
     GHashTable *olpc_invited_activities;
     GHashTable *olpc_views;
+    GHashTable *olpc_current_act;
 
     /* OLPC services */
     const gchar *olpc_gadget_buddy;
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index d52133f..9dae343 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -6,6 +6,7 @@ TWISTED_TESTS = \
 	muc/test-muc-properties.py \
 	muc/test-muc.py \
 	olpc/olpc-activity-search.py \
+	olpc/current-activity.py \
 	olpc/olpc-muc-invitation.py \
 	olpc/olpc-muc-prop-change.py \
 	olpc/test-olpc-bundle.py \
diff --git a/tests/twisted/olpc/current-activity.py b/tests/twisted/olpc/current-activity.py
new file mode 100644
index 0000000..2a1bb6e
--- /dev/null
+++ b/tests/twisted/olpc/current-activity.py
@@ -0,0 +1,237 @@
+"""
+test OLPC Buddy properties current activity
+"""
+
+import dbus
+
+from servicetest import call_async, EventPattern
+from gabbletest import exec_test, make_result_iq, acknowledge_iq
+from twisted.words.protocols.jabber.client import IQ
+
+from twisted.words.xish import domish, xpath
+
+NS_OLPC_BUDDY_PROPS = "http://laptop.org/xmpp/buddy-properties"
+NS_OLPC_ACTIVITIES = "http://laptop.org/xmpp/activities"
+NS_OLPC_CURRENT_ACTIVITY = "http://laptop.org/xmpp/current-activity"
+NS_OLPC_ACTIVITY_PROPS = "http://laptop.org/xmpp/activity-properties"
+NS_OLPC_BUDDY = "http://laptop.org/xmpp/buddy"
+NS_OLPC_ACTIVITY = "http://laptop.org/xmpp/activity"
+
+NS_PUBSUB = "http://jabber.org/protocol/pubsub"
+NS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
+NS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
+NS_AMP = "http://jabber.org/protocol/amp"
+NS_STANZA = "urn:ietf:params:xml:ns:xmpp-stanzas"
+
+def test(q, bus, conn, stream):
+    conn.Connect()
+
+    _, iq_event, disco_event = q.expect_many(
+        EventPattern('dbus-signal', signal='StatusChanged', args=[0, 1]),
+        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
+            query_name='vCard'),
+        EventPattern('stream-iq', to='localhost', query_ns=NS_DISCO_ITEMS))
+
+    acknowledge_iq(stream, iq_event.stanza)
+
+    # announce Gadget service
+    reply = make_result_iq(stream, disco_event.stanza)
+    query = xpath.queryForNodes('/iq/query', reply)[0]
+    item = query.addElement((None, 'item'))
+    item['jid'] = 'gadget.localhost'
+    stream.send(reply)
+
+    # wait for Gadget disco#info query
+    event = q.expect('stream-iq', to='gadget.localhost', query_ns=NS_DISCO_INFO)
+    reply = make_result_iq(stream, event.stanza)
+    query = xpath.queryForNodes('/iq/query', reply)[0]
+    identity = query.addElement((None, 'identity'))
+    identity['category'] = 'collaboration'
+    identity['type'] = 'gadget'
+    identity['name'] = 'OLPC Gadget'
+    feature = query.addElement((None, 'feature'))
+    feature['var'] = NS_OLPC_BUDDY
+    feature = query.addElement((None, 'feature'))
+    feature['var'] = NS_OLPC_ACTIVITY
+    stream.send(reply)
+
+    buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
+    gadget_iface = dbus.Interface(conn, 'org.laptop.Telepathy.Gadget')
+
+    handles = {}
+
+    # Alice is one of our friend so we receive her PEP notifications
+    handles['alice'] = conn.RequestHandles(1, ['alice at localhost'])[0]
+
+    # Try to get Alice's currrent-activity
+    call_async(q, buddy_info_iface, "GetCurrentActivity", handles['alice'])
+
+    # Alice's current-activity is not in the cache so Gabble sends a PEP query
+    event = q.expect('stream-iq', iq_type='get', query_name='pubsub')
+    reply = make_result_iq(stream, event.stanza)
+    reply['from'] = 'alice at localhost'
+    pubsub = reply.firstChildElement()
+    items = pubsub.addElement((None, 'items'))
+    items['node'] = NS_OLPC_CURRENT_ACTIVITY
+    item = items.addElement((None, 'item'))
+    item['id'] = 'itemID'
+    activity = item.addElement((NS_OLPC_CURRENT_ACTIVITY, 'activity'))
+    activity['room'] = 'room1 at conference.localhost'
+    activity['type'] = 'activity1'
+    reply.send()
+
+    event = q.expect('dbus-return', method='GetCurrentActivity')
+    id, handles['room1'] = event.value
+    assert id == 'activity1'
+    assert conn.InspectHandles(2, [handles['room1']]) == \
+            ['room1 at conference.localhost']
+
+    # Retry to get Alice's current-activity
+    # Alice's current-activity is now in the cache so Gabble doesn't
+    # send PEP query
+    assert buddy_info_iface.GetCurrentActivity(handles['alice']) == \
+            ('activity1', handles['room1'])
+
+    # Alice changed her current-activity
+    message = domish.Element(('jabber:client', 'message'))
+    message['from'] = 'alice at localhost'
+    message['to'] = 'test at localhost'
+    event = message.addElement(('http://jabber.org/protocol/pubsub#event',
+        'event'))
+
+    items = event.addElement((None, 'items'))
+    items['node'] = NS_OLPC_CURRENT_ACTIVITY
+    item = items.addElement((None, 'item'))
+
+    activity = item.addElement((NS_OLPC_CURRENT_ACTIVITY, 'activity'))
+    activity['room'] = 'room2 at conference.localhost'
+    activity['type'] = 'activity2'
+
+    stream.send(message)
+
+    event = q.expect('dbus-signal', signal='CurrentActivityChanged')
+    contact, id, handles['room2'] = event.args
+    assert contact == handles['alice']
+    assert id == 'activity2'
+    assert conn.InspectHandles(2, [handles['room2']]) == \
+            ['room2 at conference.localhost']
+
+    # Get Alice's current-activity as the cache have to be updated
+    assert buddy_info_iface.GetCurrentActivity(handles['alice']) == \
+            ('activity2', handles['room2'])
+
+    # request a activity view containing only Bob and one
+    # activity in it.
+    call_async(q, gadget_iface, 'RequestRandomActivities', 1)
+
+    # TODO: would be cool to have to view test helper code
+    iq_event, return_event = q.expect_many(
+    EventPattern('stream-iq', to='gadget.localhost',
+        query_ns=NS_OLPC_ACTIVITY),
+    EventPattern('dbus-return', method='RequestRandomActivities'))
+
+    view = iq_event.stanza.firstChildElement()
+    assert view.name == 'view'
+    assert view['id'] == '0'
+    random = xpath.queryForNodes('/iq/view/random', iq_event.stanza)
+    assert len(random) == 1
+    assert random[0]['max'] == '1'
+
+    # reply to random query
+    reply = make_result_iq(stream, iq_event.stanza)
+    reply['from'] = 'gadget.localhost'
+    reply['to'] = 'test at localhost'
+    view = xpath.queryForNodes('/iq/view', reply)[0]
+    activity = view.addElement((None, "activity"))
+    activity['room'] = 'room3 at conference.localhost'
+    activity['id'] = 'activity3'
+    buddy = activity.addElement((None, 'buddy'))
+    buddy['jid'] = 'bob at localhost'
+    stream.send(reply)
+
+    # Gadget sends us a current-activity change concerning a
+    # known activity
+    message = domish.Element(('jabber:client', 'message'))
+    message['from'] = 'gadget.localhost'
+    message['to'] = 'alice at localhost'
+    message['type'] = 'notice'
+
+    change = message.addElement((NS_OLPC_BUDDY, 'change'))
+    change['jid'] = 'bob at localhost'
+    change['id'] = '0'
+    activity = change.addElement((NS_OLPC_CURRENT_ACTIVITY, 'activity'))
+    activity['type'] = 'activity3'
+    activity['room'] = 'room3 at conference.localhost'
+
+    amp = message.addElement((NS_AMP, 'amp'))
+    rule = amp.addElement((None, 'rule'))
+    rule['condition'] = 'deliver-at'
+    rule['value'] = 'stored'
+    rule['action'] ='error'
+    stream.send(message)
+
+    # Gadget notifies us about the change
+    event = q.expect('dbus-signal', signal='CurrentActivityChanged')
+    handles['bob'], id, handles['room3'] = event.args
+    assert id == 'activity3'
+    assert conn.InspectHandles(1, [handles['bob']]) == \
+            ['bob at localhost']
+    assert conn.InspectHandles(2, [handles['room3']]) == \
+            ['room3 at conference.localhost']
+
+    # And the cache was properly updated
+    assert buddy_info_iface.GetCurrentActivity(handles['bob']) == \
+            ('activity3', handles['room3'])
+
+    # Gadget sends us a current-activity change concerning an
+    # unknown activity
+    message = domish.Element(('jabber:client', 'message'))
+    message['from'] = 'gadget.localhost'
+    message['to'] = 'alice at localhost'
+    message['type'] = 'notice'
+
+    change = message.addElement((NS_OLPC_BUDDY, 'change'))
+    change['jid'] = 'bob at localhost'
+    change['id'] = '0'
+    activity = change.addElement((NS_OLPC_CURRENT_ACTIVITY, 'activity'))
+    activity['type'] = 'activity4'
+    activity['room'] = 'room4 at conference.localhost'
+
+    amp = message.addElement((NS_AMP, 'amp'))
+    rule = amp.addElement((None, 'rule'))
+    rule['condition'] = 'deliver-at'
+    rule['value'] = 'stored'
+    rule['action'] ='error'
+    stream.send(message)
+
+    # Gadget changed Alice's current-activity to none as it doesn't
+    # know the activity
+    event = q.expect('dbus-signal', signal='CurrentActivityChanged',
+            args=[handles['bob'], '', 0])
+
+    call_async(q, buddy_info_iface, "GetCurrentActivity", handles['bob'])
+
+    # Bob's current-activity is not in the cache anymore so Gabble try
+    # to send a PEP query
+    event = q.expect('stream-iq', iq_type='get', query_name='pubsub')
+    iq = event.stanza
+
+    # Alice is not Bob's friend so she can't query his PEP node
+    reply = IQ(stream, "error")
+    reply['id'] = iq['id']
+    reply['from'] = iq['to']
+    pubsub = reply.addElement((NS_PUBSUB, 'pubsub'))
+    items = pubsub.addElement((None, 'items'))
+    items['node'] = NS_OLPC_CURRENT_ACTIVITY
+    error = reply.addElement((None, 'error'))
+    error['type'] = 'auth'
+    error.addElement((NS_STANZA, 'not-authorized'))
+    error.addElement(("%s#errors" % NS_PUBSUB, 'presence-subscription-required'))
+    stream.send(reply)
+
+    # so Bob is considererd without current activity
+    q.expect('dbus-return', method='GetCurrentActivity',
+            value=('', 0))
+
+if __name__ == '__main__':
+    exec_test(test)
-- 
1.5.6.5




More information about the Telepathy-commits mailing list