[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