[sugar] review: Guillaume's 'activity' branch of Gadget

Dafydd Harries dafydd.harries at collabora.co.uk
Tue Jun 10 09:42:33 EDT 2008


> diff --git a/gadget/component.py b/gadget/component.py
> index b5fc702..dd85a03 100644
> --- a/gadget/component.py
> +++ b/gadget/component.py
> @@ -92,8 +92,9 @@ class Room(object):
>      def __init__(self, own_nick):
>          self.own_nick = own_nick
>          self.membership = None
> -        self.members = {}
> +        self.members = set()
>          self.properties = {}
> +        self.id = None
>  

Would it be possible to just have a Room.activity property and use that to
store members/id?

>  class GadgetService(component.Service):
>      debug = False
> @@ -333,7 +334,7 @@ class GadgetService(component.Service):
>      def message(self, stanza):
>          type = stanza.getAttribute('type', 'normal')
>  
> -        if type in ('chat', 'groupchat'):
> +        if type in ('chat'):
>              return

This doesn't do quite what you want. "()" doesn't create a tuple, "," does, so
this is the same as "type in 'chat'", which will be true if type is a
substring of "chat". Better to just say "==".

>  
>          if type == 'normal':
> @@ -343,12 +344,17 @@ class GadgetService(component.Service):
>                      return
>                  elif (elem.uri == ns.ACTIVITY_PROP and
>                        elem.name == 'properties'):
> -                    self.message_activity_properties(stanza, elem)
> +                    self.message_pre_invite_activity_properties(stanza, elem)
>                      return
>                  elif elem.uri == ns.PUBSUB_EVENT and elem.name == 'event':
>                      self.message_pubsub_notification(stanza, elem)
>                      return
>  
> +        elif type == 'groupchat':
> +            for elem in stanza.elements():
> +                if elem.uri == ns.ACTIVITY_PROP and elem.name == 'properties':
> +                    self.message_activity_properties(stanza, elem)

You should return here. Perhaps we should dispatch messages more like we
dispatch IQs, i.e. using a dict.

> +
>          # FIXME: handle close query stanza
>          log.msg('got unhandled <message>')
>  
> @@ -357,7 +363,7 @@ class GadgetService(component.Service):
>              if elem.uri == ns.MUC_USER and elem.name == 'invite':
>                  self.message_muc_invite(stanza, elem)
>  
> -    def create_room(self, jid, properties):
> +    def create_room(self, jid, properties, id):

Hmm, can we put the id before the properties? I think it's more aesthetically
pleasing that way.

>          if jid in self.mucs:
>              # We already have a room by this name.
>              return
> @@ -366,9 +372,10 @@ class GadgetService(component.Service):
>          # join it.
>          room = Room('inspector')
>          room.properties = properties
> +        room.id = id
>          self.mucs[jid] = room
>  
> -    def message_activity_properties(self, stanza, properties_elem):
> +    def message_pre_invite_activity_properties(self, stanza, properties_elem):
>          # XXX Restrictions on from address?
>  
>          try:
> @@ -384,7 +391,7 @@ class GadgetService(component.Service):
>          if not (properties and activity and room_jid):
>              return
>  
> -        self.create_room(room_jid, properties)
> +        self.create_room(room_jid, properties, activity)

Perhaps we should rename 'activity' to 'activity_id' for clarity.

>      def message_muc_invite(self, stanza, invite):
>          to = stanza.getAttribute('to')
> @@ -502,17 +509,54 @@ class GadgetService(component.Service):
>                  # Presence for our in-room self; we've finished joining the
>                  # room.
>                  room.membership = 'joined'
> +
> +                # activities are created and added to the model when the
> +                # inspector actually joined the muc.

English nitpicks: Your tenses don't agree here. "are created" ... "joined".
s/joined/join/. If you use a full stop, you should also capitalise the first
letter.

> +                # If we'll do it before, we won't be able to properly track
> +                # activity's properties and members.

I don't understand this comment.

> +                activity = self.model.activity_add(room.id, room_jid,
> +                        room.properties)
> +
> +                # Activity and Room now share the same set() object
> +                activity.members = room.members
>                  return
>  
> +        item = xpath_query('/presence/x[@xmlns="%s"]/item' % ns.MUC_USER,
> +                stanza)
> +        if not item or item[0].getAttribute('jid') is None:
> +            log.msg("full jid of %s missing. Can't update activity members"
> +                    % jid.resource)

English nitpicks: you should capitalise "full" and have a full stop after
the second sentence.

> +            return
> +
> +        full_jid = JID(item[0]['jid']).userhost()

Hmm, "full_jid" seems like a bad name, since it implies a JID with a resource.
Perhaps it should be "real_jid" or just "jid".

> +
> +        activities = self.model.activity_by_room(room_jid)
> +
>          if type is None:
>              log.msg('room %s has member %s' % (room_jid, jid.resource))
> -            room.members[jid.resource] = True
> +            room.members.add(full_jid)
> +
>          elif type == 'unavailable':
>              if room_jid in self.mucs:
>                  room = self.mucs[room_jid]
>  
> -                if jid.resource in room.members:
> -                    del room.members[jid.resource]
> +                if full_jid in room.members:
> +                    room.members.remove(full_jid)
> +
> +                    if len(room.members) == 0:
> +                        self.leave_activity(room_jid, room.id)
> +
> +    def leave_activity(self, room_jid, activity_id):

If we have Room.activity you won't need to pass around the ID here.

> +        log.msg('leave room %s' % room_jid)
> +

Should be "leaving room".

> +        presence = domish.Element((None, 'presence'))
> +        presence['to'] = '%s/inspector' % room_jid

You should use the JID class to generate the JID, and use Room.own_nick for
the resource.

> +        presence['type'] = 'unavailable'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        self.send(presence)
> +
> +        self.model.activity_remove(activity_id)
> +        del self.mucs[room_jid]
>  
>      def message_pubsub_notification(self, stanza, event):
>          items = event.firstChildElement()
> @@ -533,3 +577,33 @@ class GadgetService(component.Service):
>                  self.model.buddy_add(jid, props)
>  
>              # FIXME: send notification messages to interested buddies
> +
> +    def message_activity_properties(self, stanza, properties_elem):
> +        id = properties_elem.getAttribute('activity')
> +        room_jid = properties_elem.getAttribute('room')
> +        jid = JID(stanza['from'])
> +
> +        if room_jid != jid.userhost():
> +            log.msg("Ignored activity properties message from %s trying to change properties for another room"
> +                    % stanza['from'])

Can you wrap this to 79 characters? You can just split the string literal in
two.

> +            return
> +
> +        try:
> +            properties = parse_properties(properties_elem)
> +        except PropertyTypeError:
> +            log.msg("Ignored activity properties message from %s containing an invalid property"
> +                    % stanza['from'])

Same here.

> +            return
> +
> +        if not self.model.has_activity(id):
> +            log.msg("Ignored activity properties message from %s about an invalid activity: %s "
> +                    % stanza['from'], id)

And here.

> +            return
> +
> +        # is activity private?
> +        if properties.get('private') in (('bool', '1'), ('bool', 'true')):

Perhaps we should do some kind of normalisation in parse_properties, and maybe
ignore malformed values.

> +            log.msg('Activity %s is not private. Leave it' % id)

s/Leave/Leaving/.

> +            self.leave_activity(room_jid, id)
> +            return
> +
> +        self.model.activity_update(id, properties)
> diff --git a/gadget/model.py b/gadget/model.py
> index faf4967..1b33734 100644
> --- a/gadget/model.py
> +++ b/gadget/model.py
> @@ -3,7 +3,7 @@ import random
>  class Activity(object):
>      __slots__ = ('id', 'room', 'properties', 'members')
>  
> -    def __init__(self, id, room, properties=None):
> +    def __init__(self, id, room, properties=None, members=None):
>          self.id = id
>          self.room = room
>  
> @@ -12,7 +12,10 @@ class Activity(object):
>          else:
>              self.properties = {}
>  
> -        self.members = []
> +        if members:
> +            self.members = members
> +        else:
> +            self.members = set()
>  
>      def __repr__(self):
>          return '<Activity %r %r>' % (self.id, self.room)
> @@ -85,7 +88,7 @@ class GadgetModel(object):
>          # FIXME: this is not very efficient
>          results = []
>          for activity in self.activities.itervalues():
> -            if set(buddies).issubset(set(activity.members)):
> +            if set(buddies).issubset(activity.members):
>                      results.append(activity)
>  
>          return results
> @@ -197,3 +200,6 @@ class GadgetModel(object):
>  
>      def has_buddy(self, jid):
>          return self.buddies.has_key(jid)
> +
> +    def has_activity(self, id):
> +        return self.activities.has_key(id)
> diff --git a/gadget/test_activity_query.py b/gadget/test_activity_query.py
> index dc8dc58..707c332 100644
> --- a/gadget/test_activity_query.py
> +++ b/gadget/test_activity_query.py
> @@ -25,7 +25,7 @@ def parse_activities_result(stanza):
>                  if child.name == 'member':
>                      jid = child.getAttribute('jid')
>                      assert jid
> -                    activities[-1].members.append(jid)
> +                    activities[-1].members.add(jid)
>  
>      return sorted(activities)
>  
> @@ -79,8 +79,8 @@ class ActivityIqTest3(TestCase):
>  
>          model = self.service.model
>          activity = model.activity_add(1, 'foo', {})
> -        activity.members.append('bob at foo.org')
> -        activity.members.append('tom at foo.org')
> +        activity.members.add('bob at foo.org')
> +        activity.members.add('tom at foo.org')
>  
>          iq = IQ(self.server, "get")
>          iq['from'] = 'bob at foo.org'
> @@ -93,7 +93,7 @@ class ActivityIqTest3(TestCase):
>          activities = parse_activities_result(stanza)
>          assert len(activities) == 1
>          assert activities[0].id == '1'
> -        assert activities[0].members == ['bob at foo.org', 'tom at foo.org']
> +        assert activities[0].members == set(['bob at foo.org', 'tom at foo.org'])
>          self.done.callback(None)
>  
>  class ActivityIqTest4(TestCase):
> @@ -161,12 +161,12 @@ class ActivityIqTest6(TestCase):
>  
>          model = self.service.model
>          activity = model.activity_add(1, 'foo', {'type': ('str', 'Paint')})
> -        activity.members.append('alice at foo.org')
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('alice at foo.org')
> +        activity.members.add('bob at foo.org')
>          activity = model.activity_add(2, 'bar', {'type': ('str', 'Connect')})
> -        activity.members.append('alice at foo.org')
> +        activity.members.add('alice at foo.org')
>          activity = model.activity_add(3, 'test', {'type': ('str', 'Paint')})
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('bob at foo.org')
>  
>          iq = IQ(self.server, "get")
>          iq['from'] = 'bob at foo.org'
> @@ -194,12 +194,12 @@ class ActivityIqTest7(TestCase):
>  
>          model = self.service.model
>          activity = model.activity_add(1, 'foo', {'type': ('str', 'Paint')})
> -        activity.members.append('alice at foo.org')
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('alice at foo.org')
> +        activity.members.add('bob at foo.org')
>          activity = model.activity_add(2, 'bar', {'type': ('str', 'Connect')})
> -        activity.members.append('alice at foo.org')
> +        activity.members.add('alice at foo.org')
>          activity = model.activity_add(3, 'test', {'type': ('str', 'Paint')})
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('bob at foo.org')
>  
>          iq = IQ(self.server, "get")
>          iq['from'] = 'bob at foo.org'
> @@ -231,12 +231,12 @@ class ActivityIqTest8(TestCase):
>  
>          model = self.service.model
>          activity = model.activity_add(1, 'foo', {'type': ('str', 'Paint')})
> -        activity.members.append('alice at foo.org')
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('alice at foo.org')
> +        activity.members.add('bob at foo.org')
>          activity = model.activity_add(2, 'bar', {'type': ('str', 'Connect')})
> -        activity.members.append('alice at foo.org')
> +        activity.members.add('alice at foo.org')
>          activity = model.activity_add(3, 'test', {'type': ('str', 'Paint')})
> -        activity.members.append('bob at foo.org')
> +        activity.members.add('bob at foo.org')
>  
>          iq = IQ(self.server, "get")
>          iq['from'] = 'bob at foo.org'
> diff --git a/gadget/test_component.py b/gadget/test_component.py
> index 4325593..00d5270 100644
> --- a/gadget/test_component.py
> +++ b/gadget/test_component.py
> @@ -9,7 +9,7 @@ from twisted.words.protocols.jabber.client import IQ
>  
>  from component import GadgetService, parse_properties, properties_to_xml,\
>          PropertyTypeError
> -from model import GadgetModel
> +from model import GadgetModel, Activity
>  
>  import ns
>  
> @@ -214,7 +214,7 @@ class RoomMembershipTest(TestCase):
>          self.done = defer.Deferred()
>          self.server.addOnetimeObserver('//presence', self.presence)
>          # Simulate invitation process.
> -        self.service.create_room('chat at conf.foo.org', {})
> +        self.service.create_room('chat at conf.foo.org', {}, 'activity1')
>          self.service.invited('inspector at gadget.bar.org', 'chat at conf.foo.org')
>          return self.done
>  
> @@ -222,11 +222,15 @@ class RoomMembershipTest(TestCase):
>          presence = domish.Element((ns.CLIENT, 'presence'))
>          presence['from'] = 'chat at conf.foo.org/amy'
>          x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'amy at foo.org/resource'
>          self.server.send(presence)
>  
>          presence = domish.Element((ns.CLIENT, 'presence'))
>          presence['from'] = 'chat at conf.foo.org/bob'
>          x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'bob at foo.org/resource'
>          self.server.send(presence)

We should probably have a test that includes a MUC presence message without a
jid.

Perhaps we should factor out the code for creating MUC presence messages here.

>          presence = domish.Element((ns.CLIENT, 'presence'))
> @@ -239,22 +243,30 @@ class RoomMembershipTest(TestCase):
>      def blah(self):
>          assert 'chat at conf.foo.org' in self.service.mucs
>          room = self.service.mucs['chat at conf.foo.org']
> -        assert sorted(room.members.keys()) == ['amy', 'bob']
> +        assert room.members == set(['amy at foo.org', 'bob at foo.org'])
> +        activity = self.model.activity_by_id('activity1')
> +        assert activity.members == set(['amy at foo.org', 'bob at foo.org'])
>  
>          # Amy leaves the room.
>          presence = domish.Element((ns.CLIENT, 'presence'))
>          presence['from'] = 'chat at conf.foo.org/amy'
>          presence['type'] = 'unavailable'
>          x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'amy at foo.org/resource'
>          self.server.send(presence)
>  
>          # Cat joins the room.
>          presence = domish.Element((ns.CLIENT, 'presence'))
>          presence['from'] = 'chat at conf.foo.org/cat'
>          x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'cat at foo.org/resource'
>          self.server.send(presence)
>  
> -        assert sorted(room.members.keys()) == ['bob', 'cat']
> +        assert room.members == set(['bob at foo.org', 'cat at foo.org'])
> +        activity = self.model.activity_by_id('activity1')
> +        assert activity.members == set(['bob at foo.org', 'cat at foo.org'])
>          self.done.callback(None)
>  
>  class PropertiesTest(TestCase):
> @@ -528,3 +540,224 @@ class BuddyPropertiesChangeTest(TestCase):
>          assert buddy.properties == {'color': ('str', 'blue'),
>              'key': ('str', 'my key')}
>          self.done.callback(None)
> +
> +class ActivityPropertiesTest(TestCase):
> +    def runTest(self):
> +        self.done = defer.Deferred()
> +        self.server.addOnetimeObserver('//presence', self.presence)
> +        # Simulate invitation process.
> +        self.service.create_room('chat at conf.localhost', {}, 'activity1')
> +        self.service.invited('inspector at gadget.localhost', 'chat at conf.localhost')
> +        return self.done
> +
> +    def presence(self, _):
> +        # bob muc presence

s/bob/Bob's/

> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/bob'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'bob at foo.org/resource'
> +        self.server.send(presence)
> +
> +        # gadget muc presence

s/gadget/Gadget's/

> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/inspector'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        self.server.send(presence)
> +
> +        reactor.callLater(0, self.later)
> +
> +    def later(self):
> +        assert 'chat at conf.localhost' in self.service.mucs
> +        room = self.service.mucs['chat at conf.localhost']
> +        assert room.members == set(['bob at foo.org'])
> +
> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost', {},
> +                set(['bob at foo.org']))
> +
> +        # bob change activity properties

s/change/changes/

> +        message = domish.Element((None, 'message'))
> +        message['type'] = 'groupchat'
> +        message['to'] = 'chat at conf.localhost'
> +        message['from'] = 'chat at conf.localhost/bob'
> +        properties = message.addElement((ns.ACTIVITY_PROP, 'properties'))
> +        properties['activity'] = 'activity1'
> +        properties['room'] = 'chat at conf.localhost'
> +        property = properties.addElement((None, 'property'))
> +        property['type'] = 'str'
> +        property['name'] = 'title'
> +        property.addContent('Test Activity')
> +        self.server.send(message)
> +
> +        reactor.callLater(0, self.later2)
> +
> +    def later2(self):
> +

Delete this blank line.

> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost',
> +                {'title': ('str', 'Test Activity')}, set(['bob at foo.org']))
> +
> +        self.done.callback(None)
> +
> +class ActivityLeaveTest(TestCase):
> +    """Gaget leave and remove activity when the latest buddy left"""

"Gadget leaves an activity and forgets about it when the last buddy leaves."

> +    def runTest(self):
> +        self.done = defer.Deferred()
> +        self.server.addOnetimeObserver('//presence', self.presence)
> +        # Simulate invitation process.
> +        self.service.create_room('chat at conf.localhost', {}, 'activity1')
> +        self.service.invited('inspector at gadget.localhost', 'chat at conf.localhost')

Needs wrapping.

> +        return self.done
> +
> +    def presence(self, _):
> +        # georges muc presence

George's.

> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/georges'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'georges at foo.org/resource'
> +        self.server.send(presence)
> +
> +        # gadget muc presence
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/inspector'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        self.server.send(presence)
> +
> +        reactor.callLater(0, self.later)
> +
> +    def later(self):
> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost', {},
> +                set(['georges at foo.org']))
> +
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/georges'
> +        presence['type'] = 'unavailable'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'georges at foo.org/resource'
> +        self.server.send(presence)
> +
> +        reactor.callLater(0, self.later2)
> +
> +    def later2(self):
> +
> +        assert not self.model.has_activity('activity1')
> +        assert len(self.service.mucs) == 0
> +        self.done.callback(None)
> +
> +class ActivityPrivate(TestCase):

Maybe "ActivityBecomesPrivate"?

> +    """Gaget leave and remove activity when it becomes private"""
> +    def runTest(self):
> +        self.done = defer.Deferred()
> +        self.server.addOnetimeObserver('//presence', self.presence)
> +        # Simulate invitation process.
> +        self.service.create_room('chat at conf.localhost', {}, 'activity1')
> +        self.service.invited('inspector at gadget.localhost', 'chat at conf.localhost')
> +        return self.done
> +
> +    def presence(self, _):
> +        # georges muc presence
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/georges'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'georges at foo.org/resource'
> +        self.server.send(presence)
> +
> +        # gadget muc presence
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/inspector'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        self.server.send(presence)
> +
> +        reactor.callLater(0, self.later)
> +
> +    def later(self):
> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost', {},
> +                set(['georges at foo.org']))
> +
> +        # Georges makes activity private
> +        message = domish.Element((None, 'message'))
> +        message['type'] = 'groupchat'
> +        message['to'] = 'chat at conf.localhost'
> +        message['from'] = 'chat at conf.localhost/georges'
> +        properties = message.addElement((ns.ACTIVITY_PROP, 'properties'))
> +        properties['activity'] = 'activity1'
> +        properties['room'] = 'chat at conf.localhost'
> +        property = properties.addElement((None, 'property'))
> +        property['type'] = 'bool'
> +        property['name'] = 'private'
> +        property.addContent('1')
> +        self.server.send(message)
> +
> +        reactor.callLater(0, self.later2)
> +
> +    def later2(self):
> +
> +        assert not self.model.has_activity('activity1')
> +        assert len(self.service.mucs) == 0
> +        self.done.callback(None)
> +
> +class ActivityPropertiesSpoofTest(TestCase):
> +    def runTest(self):
> +        self.done = defer.Deferred()
> +        self.server.addOnetimeObserver('//presence', self.presence)
> +        # Simulate invitation process.
> +        self.service.create_room('chat at conf.localhost', {}, 'activity1')
> +        self.service.invited('inspector at gadget.localhost', 'chat at conf.localhost')
> +        return self.done
> +
> +    def presence(self, _):
> +        # bob muc presence
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/bob'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        item = x.addElement('item')
> +        item['jid'] = 'bob at foo.org/resource'
> +        self.server.send(presence)
> +
> +        # gadget muc presence
> +        presence = domish.Element((ns.CLIENT, 'presence'))
> +        presence['from'] = 'chat at conf.localhost/inspector'
> +        x = presence.addElement((ns.MUC_USER, 'x'))
> +        self.server.send(presence)
> +
> +        reactor.callLater(0, self.later)
> +
> +    def later(self):
> +        assert 'chat at conf.localhost' in self.service.mucs
> +        room = self.service.mucs['chat at conf.localhost']
> +        assert room.members == set(['bob at foo.org'])
> +
> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost', {},
> +                set(['bob at foo.org']))
> +
> +        # bad oscar tries to change properties but is not in the muc
> +        message = domish.Element((None, 'message'))
> +        message['type'] = 'groupchat'
> +        message['to'] = 'anotherchat at conf.localhost'
> +        message['from'] = 'anotherchat at conf.localhost/oscar'
> +        properties = message.addElement((ns.ACTIVITY_PROP, 'properties'))
> +        properties['activity'] = 'activity1'
> +        properties['room'] = 'chat at conf.localhost'
> +        property = properties.addElement((None, 'property'))
> +        property['type'] = 'str'
> +        property['name'] = 'title'
> +        property.addContent('Test Activity')
> +        self.server.send(message)
> +
> +        reactor.callLater(0, self.later2)
> +
> +    def later2(self):
> +
> +        activity = self.service.model.activity_by_id('activity1')
> +        assert activity == Activity('activity1', 'chat at conf.localhost', {},
> +                set(['bob at foo.org']))
> +
> +        self.done.callback(None)
> +
> diff --git a/gadget/test_model.py b/gadget/test_model.py
> index 08e2791..3d52c34 100644
> --- a/gadget/test_model.py
> +++ b/gadget/test_model.py
> @@ -6,8 +6,10 @@ from gadget.model import GadgetModel
>  class ModelTest(unittest.TestCase):
>      def test_activity_search(self):
>          model = GadgetModel()
> +        assert not model.has_activity('123')
>          activity = model.activity_add('123', 'room at conf.foo.org',
>                  {'type': ('str', 'Paint')})
> +        assert model.has_activity('123')
>  
>          self.assertEquals(activity, model.activity_by_id('123'))
>          self.assertRaises(KeyError, model.activity_by_id, '124')
> @@ -24,15 +26,15 @@ class ModelTest(unittest.TestCase):
>          results = model.activity_by_room('room at conf.bar.org')
>          self.assertEquals([], results)
>  
> -        activity.members.append('alice at foo.org')
> +        activity.members.add('alice at foo.org')
>          results = model.activity_by_participants(['alice at foo.org'])
>          self.assertEquals([activity], results)
>          results = model.activity_by_participants(['boo at foo.org'])
>          self.assertEquals([], results)
>  
>          activity2 = model.activity_add('456', 'room2 at conf.foo.org', {})
> -        activity2.members.append('alice at foo.org')
> -        activity2.members.append('bob at foo.org')
> +        activity2.members.add('alice at foo.org')
> +        activity2.members.add('bob at foo.org')
>          results = model.activity_by_participants(['alice at foo.org'])
>          self.assertEquals([activity, activity2], sorted(results))
>          results = model.activity_by_participants(

-- 
Dafydd


More information about the Sugar mailing list