Ignore:
Timestamp:
Aug 8, 2011, 1:43:09 PM (11 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
wokkel-muc-client-support-24
Message:

Add more tests, remove code redundancy and cleanup variable names.

This adds tests for timeouts in response to joins, nick and presence changes,
error responses from the room JID instead of the occupant JID and receiving
room subject changes.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/muc.py

    r144 r145  
    1010U{XEP-0045<http://www.xmpp.org/extensions/xep-0045.html>}.
    1111"""
    12 import datetime
    1312from dateutil.tz import tzutc
    1413
    1514from zope.interface import implements
    1615
    17 from twisted.internet import defer, reactor
     16from twisted.internet import defer
    1817from twisted.words.protocols.jabber import jid, error, xmlstream
    1918from twisted.words.xish import domish
     
    3938PRESENCE = '/presence'
    4039
    41 GROUPCHAT = MESSAGE +'[@type="groupchat"]/body'
    42 SUBJECT = MESSAGE +'[@type="groupchat"]/subject'
     40GROUPCHAT = MESSAGE +'[@type="groupchat"]'
    4341
    4442DEFER_TIMEOUT = 30 # basic timeout is 30 seconds
     
    263261    def toElement(self):
    264262        """
    265         Returns a L{domish.Element} representing the xml for the history options.
     263        Returns a L{domish.Element} representing the history options.
    266264        """
    267265        element = domish.Element((NS_MUC, 'history'))
     
    315313    @type state: L{int}
    316314
    317     @ivar occupantJID: The JID of the occupant in the room. Generated from roomIdentifier, service, and nick.
     315    @ivar occupantJID: The JID of the occupant in the room. Generated from
     316        roomIdentifier, service, and nick.
    318317    @type occupantJID: L{jid.JID}
    319318    """
     
    460459    timeout = None
    461460
    462     def __init__(self):
    463         XMPPHandler.__init__(self)
    464 
    465         self._rooms = {}
    466         self._deferreds = []
    467 
    468461    presenceTypeParserMap = {
    469462                'error': generic.ErrorStanza,
     
    472465                }
    473466
     467    def __init__(self, reactor=None):
     468        XMPPHandler.__init__(self)
     469
     470        self._rooms = {}
     471        self._deferreds = []
     472
     473        if reactor:
     474            self._reactor = reactor
     475        else:
     476            from twisted.internet import reactor
     477            self._reactor = reactor
     478
     479
    474480    def connectionInitialized(self):
    475481        """
     
    481487        """
    482488        xmppim.BasePresenceProtocol.connectionInitialized(self)
    483 
    484489        self.xmlstream.addObserver(GROUPCHAT, self._onGroupChat)
    485         self.xmlstream.addObserver(SUBJECT, self._onSubject)
    486         # TODO: add history
    487 
    488490        self.initialized()
    489491
     
    502504
    503505
    504     def _getRoom(self, occupantJID):
     506    def _getRoom(self, roomJID):
    505507        """
    506508        Grab a room from the room collection.
     
    511513        @type occupantJID: L{jid.JID}
    512514        """
    513         roomJID = occupantJID.userhostJID()
    514515        return self._rooms.get(roomJID)
    515516
     
    550551            self._userLeavesRoom(occupantJID)
    551552
     553
    552554    def _userLeavesRoom(self, occupantJID):
    553555        # when a user leaves a room we need to update it
    554         room = self._getRoom(occupantJID)
     556        room = self._getRoom(occupantJID.userhostJID())
    555557        if room is None:
    556558            # not in the room yet
     
    576578
    577579        # grab room
    578         room = self._getRoom(occupantJID)
     580        room = self._getRoom(occupantJID.userhostJID())
    579581        if room is None:
    580582            # not in the room yet
     
    600602        A group chat message has been received from a MUC room.
    601603
    602         There are a few event methods that may get called here. receviedGroupChat and receivedHistory
     604        There are a few event methods that may get called here.
     605        L{receivedGroupChat}, L{receivedHistory} or L{receivedHistory}.
    603606        """
    604607        message = GroupChat.fromElement(element)
    605608
    606609        occupantJID = message.sender
    607 
    608610        if not occupantJID:
    609             # need to return an error here XXX
    610611            return
    611612
    612         room = self._getRoom(occupantJID)
     613        roomJID = occupantJID.userhostJID()
     614
     615        room = self._getRoom(roomJID)
    613616        if room is None:
    614617            # not in the room yet
     
    621624            user = None
    622625
    623         if message.delay is None:
     626        if message.subject:
     627            self.receivedSubject(room, user, message.subject)
     628        elif message.delay is None:
    624629            self.receivedGroupChat(room, user, message)
    625630        else:
     
    627632
    628633
    629     def _onSubject(self, msg):
    630         """
    631         A subject has been sent from a MUC room.
    632         """
    633         if not msg.hasAttribute('from'):
    634             return
    635         occupantJID = jid.internJID(msg['from'])
    636 
    637         # grab room
    638         room = self._getRoom(occupantJID)
    639         if room is None:
    640             # not in the room yet
    641             return
    642 
    643         self.receivedSubject(occupantJID, unicode(msg.subject))
    644 
    645 
    646     def _makeTimeStamp(self, stamp=None):
    647         # create a timestamp
    648         if stamp is None:
    649             stamp = datetime.datetime.now()
    650 
    651         return stamp.strftime('%Y%m%dT%H:%M:%S')
    652 
    653 
    654     def _joinedRoom(self, d, prs):
     634    def _joinedRoom(self, presence):
    655635        """
    656636        We have presence that says we joined a room.
    657637        """
    658         occupantJID = jid.internJID(prs['from'])
    659 
    660         # check for errors
    661         if prs.hasAttribute('type') and prs['type'] == 'error':
    662             d.errback(error.exceptionFromStanza(prs))
    663         else:
    664             # change the state of the room
    665             room = self._getRoom(occupantJID)
    666             room.state = 'joined'
    667 
    668             # grab status
    669             status = getattr(prs.x, 'status', None)
    670             if status:
    671                 room.status = status.getAttribute('code', None)
    672 
    673             d.callback(room)
    674 
    675 
    676     def _leftRoom(self, d, prs):
     638        roomJID = presence.sender.userhostJID()
     639
     640        # change the state of the room
     641        room = self._getRoom(roomJID)
     642        room.state = 'joined'
     643
     644        # grab status
     645        if presence.statusCode:
     646            room.status = presence.statusCode
     647
     648        return room
     649
     650
     651    def _leftRoom(self, presence):
    677652        """
    678653        We have presence that says we left a room.
    679654        """
    680         occupantJID = jid.internJID(prs['from'])
    681 
    682         # check for errors
    683         if prs.hasAttribute('type') and prs['type'] == 'error':
    684             d.errback(error.exceptionFromStanza(prs))
    685         else:
    686             # change the state of the room
    687             self._removeRoom(occupantJID)
    688 
    689             d.callback(True)
     655        occupantJID = presence.sender
     656
     657        # change the state of the room
     658        self._removeRoom(occupantJID)
     659
     660        return True
    690661
    691662
     
    785756
    786757
    787     def sendDeferred(self,  obj, timeout):
    788         """
    789         Send data or a domish element, adding a deferred with a timeout.
    790 
    791         @param obj: The object to send over the wire.
    792         @type obj: L{domish.Element} or C{unicode}
    793 
    794         @param timeout: The number of seconds to wait before the deferred is timed out.
     758    def sendDeferred(self, stanza):
     759        """
     760        Send presence stanza, adding a deferred with a timeout.
     761
     762        @param stanza: The presence stanza to send over the wire.
     763        @type stanza: L{generic.Stanza}
     764
     765        @param timeout: The number of seconds to wait before the deferred is
     766            timed out.
    795767        @type timeout: L{int}
    796768
     
    798770        """
    799771        d = defer.Deferred()
    800         self._deferreds.append(d)
    801 
     772
     773        def onResponse(element):
     774            if element.getAttribute('type') == 'error':
     775                d.errback(error.exceptionFromStanza(element))
     776            else:
     777                d.callback(UserPresence.fromElement(element))
    802778
    803779        def onTimeout():
    804             i = 0
    805             for xd in self._deferreds:
    806                 if d == xd:
    807                     self._deferreds.pop(i)
    808                     d.errback(xmlstream.TimeoutError("Timeout waiting for response."))
    809                 i += 1
    810 
    811         call = reactor.callLater(timeout, onTimeout)
     780            d.errback(xmlstream.TimeoutError("Timeout waiting for response."))
     781
     782        call = self._reactor.callLater(DEFER_TIMEOUT, onTimeout)
    812783
    813784        def cancelTimeout(result):
     
    819790        d.addBoth(cancelTimeout)
    820791
    821         self.xmlstream.send(obj)
     792        query = "/presence[@from='%s' or (@from='%s' and @type='error')]" % (
     793                stanza.recipient.full(), stanza.recipient.userhost())
     794        self.xmlstream.addOnetimeObserver(query, onResponse, priority=1)
     795        self.xmlstream.send(stanza.toElement())
    822796        return d
    823797
     
    841815    def getConfigureForm(self, roomJID):
    842816        """
    843         Grab the configuration form from the room. This sends an iq request to the room.
     817        Grab the configuration form from the room.
     818
     819        This sends an iq request to the room.
    844820
    845821        @param roomJID: The bare JID of the room.
     
    876852            presence.history = HistoryOptions(maxstanzas=history)
    877853
    878         d = self.sendDeferred(presence.toElement(), timeout=DEFER_TIMEOUT)
    879 
    880         # add observer for joining the room
    881         query = PRESENCE + u"[@from='%s']" % room.occupantJID
    882         self.xmlstream.addOnetimeObserver(query, self._joinedRoom, 1, d)
    883 
     854        d = self.sendDeferred(presence)
     855        d.addCallback(self._joinedRoom)
    884856        return d
    885857
     
    903875
    904876
    905     def _changed(self, d, occupantJID, prs):
     877    def _changed(self, presence, occupantJID):
    906878        """
    907879        Callback for changing the nick and status.
    908880        """
    909 
    910         status = getattr(prs, 'status', None)
    911         show = getattr(prs, 'show', None)
    912 
    913         room = self._getRoom(occupantJID)
    914 
    915         user = self._changeUserStatus(room, occupantJID, status, show)
    916 
    917         d.callback(room)
     881        room = self._getRoom(occupantJID.userhostJID())
     882        self._changeUserStatus(room, occupantJID, presence.status, presence.show)
     883
     884        return room
    918885
    919886
     
    930897        @type nick: C{unicode}
    931898        """
    932 
    933 
    934899        room = self._getRoom(roomJID)
    935900
     
    939904        # Create presence
    940905        presence = BasicPresence(recipient=room.occupantJID)
    941         d = self.sendDeferred(presence.toElement(), timeout=DEFER_TIMEOUT)
    942 
    943         # Add observer for joining the room
    944         query = PRESENCE+"[@from='%s']" % (room.occupantJID.full())
    945         self.xmlstream.addOnetimeObserver(query, self._changed, 1, d, roomJID)
    946 
     906
     907        d = self.sendDeferred(presence)
     908        d.addCallback(self._changed, room.occupantJID)
    947909        return d
    948910
    949911
    950     def leave(self, occupantJID):
     912    def leave(self, roomJID):
    951913        """
    952914        Leave a MUC room.
     
    954916        See: http://xmpp.org/extensions/xep-0045.html#exit
    955917
    956         @param occupantJID: The Room JID of the room to leave.
    957         @type occupantJID: L{jid.JID}
    958         """
    959         room = self._getRoom(occupantJID)
     918        @param roomJID: The Room JID of the room to leave.
     919        @type roomJID: L{jid.JID}
     920        """
     921        room = self._getRoom(roomJID)
    960922
    961923        presence = xmppim.AvailabilityPresence(recipient=room.occupantJID,
    962924                                               available=False)
    963925
    964         d = self.sendDeferred(presence.toElement(), timeout=DEFER_TIMEOUT)
    965         # add observer for joining the room
    966         query = PRESENCE + u"[@from='%s' and @type='unavailable']" % (room.occupantJID)
    967         self.xmlstream.addOnetimeObserver(query, self._leftRoom, 1, d)
    968 
     926        d = self.sendDeferred(presence)
     927        d.addCallback(self._leftRoom)
    969928        return d
    970929
    971930
    972     def status(self, occupantJID, show=None, status=None):
     931    def status(self, roomJID, show=None, status=None):
    973932        """
    974933        Change user status.
     
    976935        See: http://xmpp.org/extensions/xep-0045.html#changepres
    977936
    978         @param occupantJID: The room jabber/xmpp entity id for the requested configuration form.
    979         @type occupantJID: L{jid.JID}
    980 
    981         @param show: The availability of the entity. Common values are xa, available, etc
     937        @param roomJID: The Room JID of the room.
     938        @type roomJID: L{jid.JID}
     939
     940        @param show: The availability of the entity. Common values are xa,
     941            available, etc
    982942        @type show: C{unicode}
    983943
     
    985945        @type show: C{unicode}
    986946        """
    987         room = self._getRoom(occupantJID)
     947        room = self._getRoom(roomJID)
    988948
    989949        presence = BasicPresence(recipient=room.occupantJID,
    990950                                 show=show, status=status)
    991951
    992         d = self.sendDeferred(presence.toElement(), timeout=DEFER_TIMEOUT)
    993 
    994         # add observer for joining the room
    995         query = PRESENCE + u"[@from='%s']" % room.occupantJID
    996         self.xmlstream.addOnetimeObserver(query, self._changed, 1, d, occupantJID)
    997 
     952        d = self.sendDeferred(presence)
     953        d.addCallback(self._changed, room.occupantJID)
    998954        return d
    999955
     
    11771133        Get an owner list from a room.
    11781134
    1179         @param roomJID: The room jabber/xmpp entity id for the requested member list.
     1135        @param roomJID: The room jabber/xmpp entity id for the requested member
     1136            list.
    11801137        @type roomJID: L{jid.JID}
    11811138        """
     
    11891146        Grab the registration form for a MUC room.
    11901147
    1191         @param room: The room jabber/xmpp entity id for the requested registration form.
     1148        @param room: The room jabber/xmpp entity id for the requested
     1149            registration form.
    11921150        @type room: L{jid.JID}
    11931151        """
Note: See TracChangeset for help on using the changeset viewer.