Ignore:
Timestamp:
Sep 2, 2011, 9:56:49 AM (9 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
wokkel-muc-client-support-24
Message:

Improve UserPresence? parsing, clean up MUCClient, approach full test coverage.

  • Parse muc#user item elements from incoming presence.
  • Properly interpret status codes in muc#user extension; there can be more than one and they should be used as integers.
  • Give observers of responses to joins, leaves and nick changes a lower priority than the general presence observers inherited from PresenceProtocol?. This ensures that the Room/User? administration in MUCClient is processed before the deferred for these actions fire.
  • Use roomJID to create Room instances, instead of the separate roomIdentifier and server arguments. Also store this roomJID on the Room.
  • Change prototype for receivedSubject to include user argument.
  • Make several callback functions nested functions within the methods that use them, instead of method on the class itself.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/test/test_muc.py

    r152 r153  
    121121
    122122
     123class UserPresenceTest(unittest.TestCase):
     124    """
     125    Tests for L{muc.UserPresence}.
     126    """
     127
     128
     129    def test_toElementUnknownChild(self):
     130        """
     131        Unknown child elements are ignored.
     132        """
     133        xml = """
     134            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     135                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     136                      to='hag66@shakespeare.lit/pda'>
     137              <x xmlns='http://jabber.org/protocol/muc#user'>
     138                <child xmlns='myns'/>
     139              </x>
     140            </presence>
     141        """
     142
     143        element = parseXml(xml)
     144        presence = muc.UserPresence.fromElement(element)
     145
     146
     147    def test_toElementStatusOne(self):
     148        """
     149        Status codes are extracted.
     150        """
     151        xml = """
     152            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     153                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     154                      to='hag66@shakespeare.lit/pda'>
     155              <x xmlns='http://jabber.org/protocol/muc#user'>
     156                <item affiliation='member' role='participant'/>
     157                <status code='110'/>
     158              </x>
     159            </presence>
     160        """
     161
     162        element = parseXml(xml)
     163        presence = muc.UserPresence.fromElement(element)
     164
     165        self.assertIn(110, presence.statusCodes)
     166
     167
     168    def test_toElementStatusMultiple(self):
     169        """
     170        Multiple status codes are all extracted.
     171        """
     172        xml = """
     173            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     174                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     175                      to='hag66@shakespeare.lit/pda'>
     176              <x xmlns='http://jabber.org/protocol/muc#user'>
     177                <item affiliation='member' role='participant'/>
     178                <status code='100'/>
     179                <status code='110'/>
     180              </x>
     181            </presence>
     182        """
     183
     184        element = parseXml(xml)
     185        presence = muc.UserPresence.fromElement(element)
     186
     187        self.assertIn(110, presence.statusCodes)
     188        self.assertIn(100, presence.statusCodes)
     189
     190
     191    def test_toElementStatusEmpty(self):
     192        """
     193        Empty status elements are ignored.
     194        """
     195        xml = """
     196            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     197                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     198                      to='hag66@shakespeare.lit/pda'>
     199              <x xmlns='http://jabber.org/protocol/muc#user'>
     200                <item affiliation='member' role='participant'/>
     201                <status/>
     202              </x>
     203            </presence>
     204        """
     205
     206        element = parseXml(xml)
     207        presence = muc.UserPresence.fromElement(element)
     208
     209        self.assertIdentical(None, presence.statusCodes)
     210
     211
     212    def test_toElementStatusBad(self):
     213        """
     214        Bad status codes are ignored.
     215        """
     216        xml = """
     217            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     218                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     219                      to='hag66@shakespeare.lit/pda'>
     220              <x xmlns='http://jabber.org/protocol/muc#user'>
     221                <item affiliation='member' role='participant'/>
     222                <status code="badvalue"/>
     223              </x>
     224            </presence>
     225        """
     226
     227        element = parseXml(xml)
     228        presence = muc.UserPresence.fromElement(element)
     229
     230        self.assertIdentical(None, presence.statusCodes)
     231
     232
     233    def test_toElementStatusUnknown(self):
     234        """
     235        Unknown status codes are still recorded in C{statusCodes}.
     236        """
     237        xml = """
     238            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     239                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
     240                      to='hag66@shakespeare.lit/pda'>
     241              <x xmlns='http://jabber.org/protocol/muc#user'>
     242                <item affiliation='member' role='participant'/>
     243                <status code="999"/>
     244              </x>
     245            </presence>
     246        """
     247
     248        element = parseXml(xml)
     249        presence = muc.UserPresence.fromElement(element)
     250
     251        self.assertIn(999, presence.statusCodes)
     252
     253
     254    def test_toElementItem(self):
     255        """
     256        Item attributes are parsed properly.
     257        """
     258        xml = """
     259            <presence from='coven@chat.shakespeare.lit/thirdwitch'
     260                      to='crone1@shakespeare.lit/desktop'>
     261              <x xmlns='http://jabber.org/protocol/muc#user'>
     262                <item affiliation='member'
     263                      jid='hag66@shakespeare.lit/pda'
     264                      role='participant'
     265                      nick='thirdwitch'/>
     266              </x>
     267            </presence>
     268        """
     269
     270        element = parseXml(xml)
     271        presence = muc.UserPresence.fromElement(element)
     272        self.assertEqual(u'member', presence.affiliation)
     273        self.assertEqual(u'participant', presence.role)
     274        self.assertEqual(JID('hag66@shakespeare.lit/pda'), presence.entity)
     275        self.assertEqual(u'thirdwitch', presence.nick)
     276
     277
    123278
    124279class MUCClientProtocolTest(unittest.TestCase):
     
    388543        self.assertNotIdentical(None, element.x, 'No muc x element')
    389544
    390         # send back user presence, nick changed
     545        # send back error presence, nick conflicted
    391546        xml = u"""
    392547            <presence from='%s/%s' type='error'>
     
    11201275        """
    11211276        # create a room
    1122         room = muc.Room(self.roomIdentifier,
    1123                         self.service,
    1124                         self.nick)
     1277        room = muc.Room(self.roomJID, self.nick)
    11251278        self.protocol._addRoom(room)
     1279        return room
    11261280
    11271281
     
    11311285        """
    11321286        verify.verifyObject(iwokkel.IMUCClient, self.protocol)
     1287
     1288
     1289    def _testPresence(self, sender='', available=True):
     1290        """
     1291        Helper for presence tests.
     1292        """
     1293        def userUpdatedStatus(room, user, show, status):
     1294            self.fail("Unexpected call to userUpdatedStatus")
     1295
     1296        def userJoinedRoom(room, user):
     1297            self.fail("Unexpected call to userJoinedRoom")
     1298
     1299        if available:
     1300            available = ""
     1301        else:
     1302            available = " type='unavailable'"
     1303
     1304        if sender:
     1305            sender = u" from='%s'" % sender
     1306
     1307        xml = u"""
     1308            <presence to='%s'%s%s>
     1309              <x xmlns='http://jabber.org/protocol/muc#user'>
     1310                <item affiliation='member' role='participant'/>
     1311              </x>
     1312            </presence>
     1313        """ % (self.userJID, sender, available)
     1314
     1315        self.protocol.userUpdatedStatus = userUpdatedStatus
     1316        self.protocol.userJoinedRoom = userJoinedRoom
     1317        self.stub.send(parseXml(xml))
     1318
     1319
     1320    def test_availableReceivedEmptySender(self):
     1321        """
     1322        Availability presence from empty sender is ignored.
     1323        """
     1324        self._testPresence(sender='')
     1325
     1326
     1327    def test_availableReceivedNotInRoom(self):
     1328        """
     1329        Availability presence from unknown entities is ignored.
     1330        """
     1331        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
     1332        self._testPresence(sender=otherOccupantJID)
     1333
     1334
     1335    def test_unavailableReceivedEmptySender(self):
     1336        """
     1337        Availability presence from empty sender is ignored.
     1338        """
     1339        self._testPresence(sender='', available=False)
     1340
     1341
     1342    def test_unavailableReceivedNotInRoom(self):
     1343        """
     1344        Availability presence from unknown entities is ignored.
     1345        """
     1346        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
     1347        self._testPresence(sender=otherOccupantJID, available=False)
     1348
     1349
     1350    def test_unavailableReceivedNotInRoster(self):
     1351        """
     1352        Availability presence from unknown entities is ignored.
     1353        """
     1354        room = self._createRoom()
     1355        user = muc.User(self.nick)
     1356        room.addUser(user)
     1357        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
     1358        self._testPresence(sender=otherOccupantJID, available=False)
    11331359
    11341360
     
    11491375
    11501376        def userJoinedRoom(room, user):
    1151             self.assertEquals(self.roomIdentifier, room.roomIdentifier,
     1377            self.assertEquals(self.roomJID, room.roomJID,
    11521378                              'Wrong room name')
    11531379            self.assertTrue(room.inRoster(user), 'User not in roster')
     
    11771403        def receivedSubject(room, user, subject):
    11781404            self.assertEquals('test', subject, "Wrong group chat message")
    1179             self.assertEquals(self.roomIdentifier, room.roomIdentifier,
     1405            self.assertEquals(self.roomJID, room.roomJID,
    11801406                              'Wrong room name')
    11811407            self.assertEquals(self.nick, user.nick)
     
    11841410        self.stub.send(parseXml(xml))
    11851411        return d
     1412
     1413
     1414    def test_receivedSubjectNotOverridden(self):
     1415        """
     1416        Not overriding receivedSubject is ok.
     1417        """
     1418        xml = u"""
     1419            <message to='%s' from='%s' type='groupchat'>
     1420              <subject>test</subject>
     1421            </message>
     1422        """ % (self.userJID, self.occupantJID)
     1423
     1424        self._createRoom()
     1425        self.stub.send(parseXml(xml))
    11861426
    11871427
     
    12001440        def receivedGroupChat(room, user, message):
    12011441            self.assertEquals('test', message.body, "Wrong group chat message")
    1202             self.assertEquals(self.roomIdentifier, room.roomIdentifier,
     1442            self.assertEquals(self.roomJID, room.roomJID,
    12031443                              'Wrong room name')
    12041444
     
    12281468
    12291469
     1470    def test_receivedGroupChatNotInRoom(self):
     1471        """
     1472        Messages received from a room we're not in are ignored.
     1473        """
     1474        xml = u"""
     1475            <message to='test@test.com' from='%s' type='groupchat'>
     1476              <body>test</body>
     1477            </message>
     1478        """ % (self.occupantJID)
     1479
     1480        def receivedGroupChat(room, user, message):
     1481            self.fail("Unexpected call to receivedGroupChat")
     1482
     1483        self.protocol.receivedGroupChat = receivedGroupChat
     1484        self.stub.send(parseXml(xml))
     1485
     1486
     1487    def test_receivedGroupChatNotOverridden(self):
     1488        """
     1489        Not overriding receivedGroupChat is ok.
     1490        """
     1491        xml = u"""
     1492            <message to='test@test.com' from='%s' type='groupchat'>
     1493              <body>test</body>
     1494            </message>
     1495        """ % (self.occupantJID)
     1496
     1497        self._createRoom()
     1498        self.stub.send(parseXml(xml))
     1499
     1500
    12301501    def test_join(self):
    12311502        """
     
    12341505
    12351506        def cb(room):
    1236             self.assertEquals(self.roomIdentifier, room.roomIdentifier)
     1507            self.assertEqual(self.roomJID, room.roomJID)
     1508            self.assertTrue('joined', room.state)
    12371509
    12381510        d = self.protocol.join(self.roomJID, self.nick)
     
    12511523
    12521524
     1525    def test_joinForbidden(self):
     1526        """
     1527        A forbidden error in response to a join errbacks with L{StanzaError}.
     1528        """
     1529
     1530        def cb(error):
     1531            self.assertEquals('forbidden', error.condition,
     1532                              'Wrong muc condition')
     1533            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
     1534
     1535
     1536        d = self.protocol.join(self.roomJID, self.nick)
     1537        self.assertFailure(d, StanzaError)
     1538        d.addCallback(cb)
     1539
     1540        # send back error, forbidden
     1541        xml = u"""
     1542            <presence from='%s' type='error'>
     1543              <error type='auth'>
     1544                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     1545              </error>
     1546            </presence>
     1547        """ % (self.occupantJID)
     1548        self.stub.send(parseXml(xml))
     1549        return d
     1550
     1551
    12531552    def test_userLeftRoom(self):
    12541553        """
     
    12691568
    12701569        def userLeftRoom(room, user):
    1271             self.assertEquals(self.roomIdentifier, room.roomIdentifier,
     1570            self.assertEquals(self.roomJID, room.roomJID,
    12721571                              'Wrong room name')
    12731572            self.assertFalse(room.inRoster(user), 'User in roster')
     
    13041603
    13051604
    1306     def test_nick(self):
    1307         """
    1308         Send a nick change to the server.
    1309         """
    1310         newNick = 'newNick'
     1605    def test_receivedHistoryNotOverridden(self):
     1606        """
     1607        Not overriding receivedHistory is ok.
     1608        """
     1609        xml = u"""
     1610            <message to='test@test.com' from='%s' type='groupchat'>
     1611              <body>test</body>
     1612              <delay xmlns='urn:xmpp:delay' stamp="2002-10-13T23:58:37Z"
     1613                                            from="%s"/>
     1614            </message>
     1615        """ % (self.occupantJID, self.userJID)
    13111616
    13121617        self._createRoom()
    1313 
    1314         def cb(room):
    1315             self.assertEquals(self.roomIdentifier, room.roomIdentifier)
    1316             self.assertEquals(newNick, room.nick)
    1317 
    1318         d = self.protocol.nick(self.roomJID, newNick)
    1319         d.addCallback(cb)
    1320 
    1321         # send back user presence, nick changed
     1618        self.stub.send(parseXml(xml))
     1619
     1620
     1621    def test_nickConflict(self):
     1622        """
     1623        If the server finds the new nick in conflict, the errback is called.
     1624        """
     1625
     1626        def cb(failure, room):
     1627            user = room.getUser(otherNick)
     1628            self.assertNotIdentical(None, user)
     1629            self.assertEqual(otherJID, user.entity)
     1630
     1631        def joined(room):
     1632            d = self.protocol.nick(room.roomJID, otherNick)
     1633            self.assertFailure(d, StanzaError)
     1634            d.addCallback(cb, room)
     1635
     1636        otherJID = JID('other@example.org/Home')
     1637        otherNick = 'otherNick'
     1638
     1639        d = self.protocol.join(self.roomJID, self.nick)
     1640        d.addCallback(joined)
     1641
     1642        # Send back other partipant's presence.
     1643        xml = u"""
     1644            <presence from='%s/%s'>
     1645              <x xmlns='http://jabber.org/protocol/muc#user'>
     1646                <item affiliation='member' role='participant' jid='%s'/>
     1647              </x>
     1648            </presence>
     1649        """ % (self.roomJID, otherNick, otherJID)
     1650        self.stub.send(parseXml(xml))
     1651
     1652        # send back user presence, they joined
    13221653        xml = u"""
    13231654            <presence from='%s/%s'>
     
    13261657              </x>
    13271658            </presence>
     1659        """ % (self.roomJID, self.nick)
     1660        self.stub.send(parseXml(xml))
     1661
     1662        room = self.protocol._getRoom(self.roomJID)
     1663
     1664        # send back error presence, nick conflicted
     1665        xml = u"""
     1666            <presence from='%s/%s' type='error'>
     1667                <x xmlns='http://jabber.org/protocol/muc'/>
     1668                <error type='cancel'>
     1669                  <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     1670                </error>
     1671            </presence>
     1672        """ % (self.roomJID, otherNick)
     1673        self.stub.send(parseXml(xml))
     1674        return d
     1675
     1676
     1677    def test_nick(self):
     1678        """
     1679        Send a nick change to the server.
     1680        """
     1681        newNick = 'newNick'
     1682
     1683        room = self._createRoom()
     1684
     1685        def joined(room):
     1686            self.assertEqual(self.roomJID, room.roomJID)
     1687            self.assertEqual(newNick, room.nick)
     1688            user = room.getUser(newNick)
     1689            self.assertNotIdentical(None, user)
     1690            self.assertEqual(newNick, user.nick)
     1691
     1692        d = self.protocol.nick(self.roomJID, newNick)
     1693        d.addCallback(joined)
     1694
     1695        # Nick should not have been changed, yet, as we haven't gotten
     1696        # confirmation, yet.
     1697
     1698        self.assertEquals(self.nick, room.nick)
     1699
     1700        # send back user presence, nick changed
     1701        xml = u"""
     1702            <presence from='%s/%s'>
     1703              <x xmlns='http://jabber.org/protocol/muc#user'>
     1704                <item affiliation='member' role='participant'/>
     1705              </x>
     1706            </presence>
    13281707        """ % (self.roomJID, newNick)
    1329         self.stub.send(parseXml(xml))
     1708
     1709        self.stub.send(parseXml(xml))
     1710        return d
     1711
     1712
     1713    def test_leave(self):
     1714        """
     1715        Client leaves a room
     1716        """
     1717        def joined(_):
     1718            return self.protocol.leave(self.roomJID)
     1719
     1720        def left(_):
     1721            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
     1722
     1723        # Join the room
     1724        d = self.protocol.join(self.roomJID, self.nick)
     1725        d.addCallback(joined)
     1726        d.addCallback(left)
     1727
     1728        # Receive presence back from the room: joined.
     1729        xml = u"""
     1730            <presence to='%s' from='%s'/>
     1731        """ % (self.userJID, self.occupantJID)
     1732        self.stub.send(parseXml(xml))
     1733
     1734        # Receive presence back from the room: left.
     1735        xml = u"""
     1736            <presence to='%s' from='%s' type='unavailable'/>
     1737        """ % (self.userJID, self.occupantJID)
     1738        self.stub.send(parseXml(xml))
     1739
    13301740        return d
    13311741
     
    13411751
    13421752        def statusChanged(room):
    1343             self.assertEquals(self.roomIdentifier, room.roomIdentifier)
     1753            self.assertEqual(self.roomJID, room.roomJID)
    13441754            user = room.getUser(self.nick)
    13451755            self.assertNotIdentical(None, user, 'User not found')
    1346             self.assertEquals('testing MUC', user.status, 'Wrong status')
    1347             self.assertEquals('xa', user.show, 'Wrong show')
     1756            self.assertEqual('testing MUC', user.status, 'Wrong status')
     1757            self.assertEqual('xa', user.show, 'Wrong show')
    13481758
    13491759        # Join the room
     
    13671777            </presence>
    13681778        """ % self.occupantJID
    1369         self.stub.send(parseXml(xml))
    1370 
    1371         return d
     1779
     1780        self.stub.send(parseXml(xml))
     1781        return d
     1782
     1783
     1784    def test_destroy(self):
     1785        """
     1786        Destroy a room.
     1787        """
     1788        def destroyed(_):
     1789            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
     1790
     1791        d = self.protocol.destroy(self.occupantJID, reason='Time to leave',
     1792                                  alternate=JID('other@%s' % self.service),
     1793                                  password='secret')
     1794        d.addCallback(destroyed)
     1795
     1796        iq = self.stub.output[-1]
     1797        response = toResponse(iq, 'result')
     1798        self.stub.send(response)
     1799        return d
Note: See TracChangeset for help on using the changeset viewer.