1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for L{twisted.words.protocols.jabber.xmlstream}.
8 from twisted.trial import unittest
10 from zope.interface.verify import verifyObject
12 from twisted.internet import defer, task
13 from twisted.internet.error import ConnectionLost
14 from twisted.internet.interfaces import IProtocolFactory
15 from twisted.python import failure
16 from twisted.test import proto_helpers
17 from twisted.words.test.test_xmlstream import GenericXmlStreamFactoryTestsMixin
18 from twisted.words.xish import domish
19 from twisted.words.protocols.jabber import error, ijabber, jid, xmlstream
23 NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls'
27 class HashPasswordTest(unittest.TestCase):
29 Tests for L{xmlstream.hashPassword}.
34 The sid and secret are concatenated to calculate sha1 hex digest.
36 hash = xmlstream.hashPassword(u"12345", u"secret")
37 self.assertEqual('99567ee91b2c7cabf607f10cb9f4a3634fa820e0', hash)
40 def test_sidNotUnicode(self):
42 The session identifier must be a unicode object.
44 self.assertRaises(TypeError, xmlstream.hashPassword, "\xc2\xb92345",
48 def test_passwordNotUnicode(self):
50 The password must be a unicode object.
52 self.assertRaises(TypeError, xmlstream.hashPassword, u"12345",
56 def test_unicodeSecret(self):
58 The concatenated sid and password must be encoded to UTF-8 before hashing.
60 hash = xmlstream.hashPassword(u"12345", u"secr\u00e9t")
61 self.assertEqual('659bf88d8f8e179081f7f3b4a8e7d224652d2853', hash)
65 class IQTest(unittest.TestCase):
67 Tests both IQ and the associated IIQResponseTracker callback.
71 authenticator = xmlstream.ConnectAuthenticator('otherhost')
72 authenticator.namespace = 'testns'
73 self.xmlstream = xmlstream.XmlStream(authenticator)
74 self.clock = task.Clock()
75 self.xmlstream._callLater = self.clock.callLater
76 self.xmlstream.makeConnection(proto_helpers.StringTransport())
77 self.xmlstream.dataReceived(
78 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
79 "xmlns='testns' from='otherhost' version='1.0'>")
80 self.iq = xmlstream.IQ(self.xmlstream, 'get')
84 self.assertEqual(self.iq['type'], 'get')
85 self.assertTrue(self.iq['id'])
89 self.xmlstream.transport.clear()
91 self.assertEqual("<iq type='get' id='%s'/>" % self.iq['id'],
92 self.xmlstream.transport.value())
95 def testResultResponse(self):
97 self.assertEqual(result['type'], 'result')
103 xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id'])
107 def testErrorResponse(self):
109 self.assertFailure(d, error.StanzaError)
112 xs.dataReceived("<iq type='error' id='%s'/>" % self.iq['id'])
116 def testNonTrackedResponse(self):
118 Test that untracked iq responses don't trigger any action.
120 Untracked means that the id of the incoming response iq is not
121 in the stream's C{iqDeferreds} dictionary.
124 xmlstream.upgradeWithIQResponseTracker(xs)
126 # Make sure we aren't tracking any iq's.
127 self.assertFalse(xs.iqDeferreds)
129 # Set up a fallback handler that checks the stanza's handled attribute.
130 # If that is set to True, the iq tracker claims to have handled the
133 self.assertFalse(getattr(iq, 'handled', False))
135 xs.addObserver("/iq", cb, -1)
137 # Receive an untracked iq response
138 xs.dataReceived("<iq type='result' id='test'/>")
141 def testCleanup(self):
143 Test if the deferred associated with an iq request is removed
144 from the list kept in the L{XmlStream} object after it has
150 xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id'])
151 self.assertNotIn(self.iq['id'], xs.iqDeferreds)
155 def testDisconnectCleanup(self):
157 Test if deferreds for iq's that haven't yet received a response
158 have their errback called on stream disconnect.
163 xs.connectionLost("Closed by peer")
164 self.assertFailure(d, ConnectionLost)
168 def testNoModifyingDict(self):
170 Test to make sure the errbacks cannot cause the iteration of the
171 iqDeferreds to blow up in our face.
175 d = xmlstream.IQ(self.xmlstream).send()
180 self.xmlstream.connectionLost("Closed by peer")
184 def testRequestTimingOut(self):
186 Test that an iq request with a defined timeout times out.
190 self.assertFailure(d, xmlstream.TimeoutError)
192 self.clock.pump([1, 60])
193 self.assertFalse(self.clock.calls)
194 self.assertFalse(self.xmlstream.iqDeferreds)
198 def testRequestNotTimingOut(self):
200 Test that an iq request with a defined timeout does not time out
201 when a response was received before the timeout period elapsed.
205 self.clock.callLater(1, self.xmlstream.dataReceived,
206 "<iq type='result' id='%s'/>" % self.iq['id'])
207 self.clock.pump([1, 1])
208 self.assertFalse(self.clock.calls)
212 def testDisconnectTimeoutCancellation(self):
214 Test if timeouts for iq's that haven't yet received a response
215 are cancelled on stream disconnect.
222 xs.connectionLost("Closed by peer")
223 self.assertFailure(d, ConnectionLost)
224 self.assertFalse(self.clock.calls)
229 class XmlStreamTest(unittest.TestCase):
231 def onStreamStart(self, obj):
232 self.gotStreamStart = True
235 def onStreamEnd(self, obj):
236 self.gotStreamEnd = True
239 def onStreamError(self, obj):
240 self.gotStreamError = True
245 Set up XmlStream and several observers.
247 self.gotStreamStart = False
248 self.gotStreamEnd = False
249 self.gotStreamError = False
250 xs = xmlstream.XmlStream(xmlstream.Authenticator())
251 xs.addObserver('//event/stream/start', self.onStreamStart)
252 xs.addObserver('//event/stream/end', self.onStreamEnd)
253 xs.addObserver('//event/stream/error', self.onStreamError)
254 xs.makeConnection(proto_helpers.StringTransportWithDisconnection())
255 xs.transport.protocol = xs
256 xs.namespace = 'testns'
261 def test_sendHeaderBasic(self):
263 Basic test on the header sent by sendHeader.
267 splitHeader = self.xmlstream.transport.value()[0:-1].split(' ')
268 self.assertIn("<stream:stream", splitHeader)
269 self.assertIn("xmlns:stream='http://etherx.jabber.org/streams'",
271 self.assertIn("xmlns='testns'", splitHeader)
272 self.assertIn("version='1.0'", splitHeader)
273 self.assertTrue(xs._headerSent)
276 def test_sendHeaderAdditionalNamespaces(self):
278 Test for additional namespace declarations.
281 xs.prefixes['jabber:server:dialback'] = 'db'
283 splitHeader = self.xmlstream.transport.value()[0:-1].split(' ')
284 self.assertIn("<stream:stream", splitHeader)
285 self.assertIn("xmlns:stream='http://etherx.jabber.org/streams'",
287 self.assertIn("xmlns:db='jabber:server:dialback'", splitHeader)
288 self.assertIn("xmlns='testns'", splitHeader)
289 self.assertIn("version='1.0'", splitHeader)
290 self.assertTrue(xs._headerSent)
293 def test_sendHeaderInitiating(self):
295 Test addressing when initiating a stream.
298 xs.thisEntity = jid.JID('thisHost')
299 xs.otherEntity = jid.JID('otherHost')
302 splitHeader = xs.transport.value()[0:-1].split(' ')
303 self.assertIn("to='otherhost'", splitHeader)
304 self.assertIn("from='thishost'", splitHeader)
307 def test_sendHeaderReceiving(self):
309 Test addressing when receiving a stream.
312 xs.thisEntity = jid.JID('thisHost')
313 xs.otherEntity = jid.JID('otherHost')
314 xs.initiating = False
317 splitHeader = xs.transport.value()[0:-1].split(' ')
318 self.assertIn("to='otherhost'", splitHeader)
319 self.assertIn("from='thishost'", splitHeader)
320 self.assertIn("id='session01'", splitHeader)
323 def test_receiveStreamError(self):
325 Test events when a stream error is received.
328 xs.dataReceived("<stream:stream xmlns='jabber:client' "
329 "xmlns:stream='http://etherx.jabber.org/streams' "
330 "from='example.com' id='12345' version='1.0'>")
331 xs.dataReceived("<stream:error/>")
332 self.assertTrue(self.gotStreamError)
333 self.assertTrue(self.gotStreamEnd)
336 def test_sendStreamErrorInitiating(self):
338 Test sendStreamError on an initiating xmlstream with a header sent.
340 An error should be sent out and the connection lost.
346 xs.sendStreamError(error.StreamError('version-unsupported'))
347 self.assertNotEqual('', xs.transport.value())
348 self.assertTrue(self.gotStreamEnd)
351 def test_sendStreamErrorInitiatingNoHeader(self):
353 Test sendStreamError on an initiating xmlstream without having sent a
356 In this case, no header should be generated. Also, the error should
357 not be sent out on the stream. Just closing the connection.
362 xs.sendStreamError(error.StreamError('version-unsupported'))
363 self.assertNot(xs._headerSent)
364 self.assertEqual('', xs.transport.value())
365 self.assertTrue(self.gotStreamEnd)
368 def test_sendStreamErrorReceiving(self):
370 Test sendStreamError on a receiving xmlstream with a header sent.
372 An error should be sent out and the connection lost.
375 xs.initiating = False
378 xs.sendStreamError(error.StreamError('version-unsupported'))
379 self.assertNotEqual('', xs.transport.value())
380 self.assertTrue(self.gotStreamEnd)
383 def test_sendStreamErrorReceivingNoHeader(self):
385 Test sendStreamError on a receiving xmlstream without having sent a
388 In this case, a header should be generated. Then, the error should
389 be sent out on the stream followed by closing the connection.
392 xs.initiating = False
394 xs.sendStreamError(error.StreamError('version-unsupported'))
395 self.assertTrue(xs._headerSent)
396 self.assertNotEqual('', xs.transport.value())
397 self.assertTrue(self.gotStreamEnd)
400 def test_reset(self):
402 Test resetting the XML stream to start a new layer.
408 self.assertNotEqual(stream, xs.stream)
409 self.assertNot(xs._headerSent)
414 Test send with various types of objects.
417 xs.send('<presence/>')
418 self.assertEqual(xs.transport.value(), '<presence/>')
421 el = domish.Element(('testns', 'presence'))
423 self.assertEqual(xs.transport.value(), '<presence/>')
426 el = domish.Element(('http://etherx.jabber.org/streams', 'features'))
428 self.assertEqual(xs.transport.value(), '<stream:features/>')
431 def test_authenticator(self):
433 Test that the associated authenticator is correctly called.
435 connectionMadeCalls = []
436 streamStartedCalls = []
437 associateWithStreamCalls = []
439 class TestAuthenticator:
440 def connectionMade(self):
441 connectionMadeCalls.append(None)
443 def streamStarted(self, rootElement):
444 streamStartedCalls.append(rootElement)
446 def associateWithStream(self, xs):
447 associateWithStreamCalls.append(xs)
449 a = TestAuthenticator()
450 xs = xmlstream.XmlStream(a)
451 self.assertEqual([xs], associateWithStreamCalls)
453 self.assertEqual([None], connectionMadeCalls)
454 xs.dataReceived("<stream:stream xmlns='jabber:client' "
455 "xmlns:stream='http://etherx.jabber.org/streams' "
456 "from='example.com' id='12345'>")
457 self.assertEqual(1, len(streamStartedCalls))
459 self.assertEqual([None], connectionMadeCalls)
463 class TestError(Exception):
468 class AuthenticatorTest(unittest.TestCase):
470 self.authenticator = xmlstream.Authenticator()
471 self.xmlstream = xmlstream.XmlStream(self.authenticator)
474 def test_streamStart(self):
476 Test streamStart to fill the appropriate attributes from the
480 xs.makeConnection(proto_helpers.StringTransport())
481 xs.dataReceived("<stream:stream xmlns='jabber:client' "
482 "xmlns:stream='http://etherx.jabber.org/streams' "
483 "from='example.org' to='example.com' id='12345' "
485 self.assertEqual((1, 0), xs.version)
486 self.assertIdentical(None, xs.sid)
487 self.assertEqual('invalid', xs.namespace)
488 self.assertIdentical(None, xs.otherEntity)
489 self.assertEqual(None, xs.thisEntity)
492 def test_streamStartLegacy(self):
494 Test streamStart to fill the appropriate attributes from the
495 stream header for a pre-XMPP-1.0 header.
498 xs.makeConnection(proto_helpers.StringTransport())
499 xs.dataReceived("<stream:stream xmlns='jabber:client' "
500 "xmlns:stream='http://etherx.jabber.org/streams' "
501 "from='example.com' id='12345'>")
502 self.assertEqual((0, 0), xs.version)
505 def test_streamBadVersionOneDigit(self):
507 Test streamStart to fill the appropriate attributes from the
508 stream header for a version with only one digit.
511 xs.makeConnection(proto_helpers.StringTransport())
512 xs.dataReceived("<stream:stream xmlns='jabber:client' "
513 "xmlns:stream='http://etherx.jabber.org/streams' "
514 "from='example.com' id='12345' version='1'>")
515 self.assertEqual((0, 0), xs.version)
518 def test_streamBadVersionNoNumber(self):
520 Test streamStart to fill the appropriate attributes from the
521 stream header for a malformed version.
524 xs.makeConnection(proto_helpers.StringTransport())
525 xs.dataReceived("<stream:stream xmlns='jabber:client' "
526 "xmlns:stream='http://etherx.jabber.org/streams' "
527 "from='example.com' id='12345' version='blah'>")
528 self.assertEqual((0, 0), xs.version)
532 class ConnectAuthenticatorTest(unittest.TestCase):
535 self.gotAuthenticated = False
536 self.initFailure = None
537 self.authenticator = xmlstream.ConnectAuthenticator('otherHost')
538 self.xmlstream = xmlstream.XmlStream(self.authenticator)
539 self.xmlstream.addObserver('//event/stream/authd', self.onAuthenticated)
540 self.xmlstream.addObserver('//event/xmpp/initfailed', self.onInitFailed)
543 def onAuthenticated(self, obj):
544 self.gotAuthenticated = True
547 def onInitFailed(self, failure):
548 self.initFailure = failure
551 def testSucces(self):
553 Test successful completion of an initialization step.
556 def initialize(self):
560 self.xmlstream.initializers = [init]
562 self.authenticator.initializeStream()
563 self.assertEqual([], self.xmlstream.initializers)
564 self.assertTrue(self.gotAuthenticated)
567 def testFailure(self):
569 Test failure of an initialization step.
572 def initialize(self):
576 self.xmlstream.initializers = [init]
578 self.authenticator.initializeStream()
579 self.assertEqual([init], self.xmlstream.initializers)
580 self.assertFalse(self.gotAuthenticated)
581 self.assertNotIdentical(None, self.initFailure)
582 self.assertTrue(self.initFailure.check(TestError))
585 def test_streamStart(self):
587 Test streamStart to fill the appropriate attributes from the
590 self.authenticator.namespace = 'testns'
592 xs.makeConnection(proto_helpers.StringTransport())
593 xs.dataReceived("<stream:stream xmlns='jabber:client' "
594 "xmlns:stream='http://etherx.jabber.org/streams' "
595 "from='example.com' to='example.org' id='12345' "
597 self.assertEqual((1, 0), xs.version)
598 self.assertEqual('12345', xs.sid)
599 self.assertEqual('testns', xs.namespace)
600 self.assertEqual('example.com', xs.otherEntity.host)
601 self.assertIdentical(None, xs.thisEntity)
602 self.assertNot(self.gotAuthenticated)
603 xs.dataReceived("<stream:features>"
604 "<test xmlns='testns'/>"
605 "</stream:features>")
606 self.assertIn(('testns', 'test'), xs.features)
607 self.assertTrue(self.gotAuthenticated)
611 class ListenAuthenticatorTest(unittest.TestCase):
613 Tests for L{xmlstream.ListenAuthenticator}
617 self.authenticator = xmlstream.ListenAuthenticator()
618 self.xmlstream = xmlstream.XmlStream(self.authenticator)
621 def test_streamStart(self):
623 Test streamStart to fill the appropriate attributes from the
627 xs.makeConnection(proto_helpers.StringTransport())
628 self.assertIdentical(None, xs.sid)
629 xs.dataReceived("<stream:stream xmlns='jabber:client' "
630 "xmlns:stream='http://etherx.jabber.org/streams' "
631 "from='example.org' to='example.com' id='12345' "
633 self.assertEqual((1, 0), xs.version)
634 self.assertNotIdentical(None, xs.sid)
635 self.assertNotEquals('12345', xs.sid)
636 self.assertEqual('jabber:client', xs.namespace)
637 self.assertIdentical(None, xs.otherEntity)
638 self.assertEqual('example.com', xs.thisEntity.host)
641 def test_streamStartUnicodeSessionID(self):
643 The generated session id must be a unicode object.
646 xs.makeConnection(proto_helpers.StringTransport())
647 xs.dataReceived("<stream:stream xmlns='jabber:client' "
648 "xmlns:stream='http://etherx.jabber.org/streams' "
649 "from='example.org' to='example.com' id='12345' "
651 self.assertIsInstance(xs.sid, unicode)
655 class TLSInitiatingInitializerTest(unittest.TestCase):
660 self.savedSSL = xmlstream.ssl
662 self.authenticator = xmlstream.Authenticator()
663 self.xmlstream = xmlstream.XmlStream(self.authenticator)
664 self.xmlstream.send = self.output.append
665 self.xmlstream.connectionMade()
666 self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
667 "xmlns:stream='http://etherx.jabber.org/streams' "
668 "from='example.com' id='12345' version='1.0'>")
669 self.init = xmlstream.TLSInitiatingInitializer(self.xmlstream)
673 xmlstream.ssl = self.savedSSL
676 def testWantedSupported(self):
678 Test start when TLS is wanted and the SSL library available.
680 self.xmlstream.transport = proto_helpers.StringTransport()
681 self.xmlstream.transport.startTLS = lambda ctx: self.done.append('TLS')
682 self.xmlstream.reset = lambda: self.done.append('reset')
683 self.xmlstream.sendHeader = lambda: self.done.append('header')
685 d = self.init.start()
686 d.addCallback(self.assertEqual, xmlstream.Reset)
687 starttls = self.output[0]
688 self.assertEqual('starttls', starttls.name)
689 self.assertEqual(NS_XMPP_TLS, starttls.uri)
690 self.xmlstream.dataReceived("<proceed xmlns='%s'/>" % NS_XMPP_TLS)
691 self.assertEqual(['TLS', 'reset', 'header'], self.done)
695 if not xmlstream.ssl:
696 testWantedSupported.skip = "SSL not available"
699 def testWantedNotSupportedNotRequired(self):
701 Test start when TLS is wanted and the SSL library available.
705 d = self.init.start()
706 d.addCallback(self.assertEqual, None)
707 self.assertEqual([], self.output)
712 def testWantedNotSupportedRequired(self):
714 Test start when TLS is wanted and the SSL library available.
717 self.init.required = True
719 d = self.init.start()
720 self.assertFailure(d, xmlstream.TLSNotSupported)
721 self.assertEqual([], self.output)
726 def testNotWantedRequired(self):
728 Test start when TLS is not wanted, but required by the server.
730 tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls'))
731 tls.addElement('required')
732 self.xmlstream.features = {(tls.uri, tls.name): tls}
733 self.init.wanted = False
735 d = self.init.start()
736 self.assertEqual([], self.output)
737 self.assertFailure(d, xmlstream.TLSRequired)
742 def testNotWantedNotRequired(self):
744 Test start when TLS is not wanted, but required by the server.
746 tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls'))
747 self.xmlstream.features = {(tls.uri, tls.name): tls}
748 self.init.wanted = False
750 d = self.init.start()
751 d.addCallback(self.assertEqual, None)
752 self.assertEqual([], self.output)
756 def testFailed(self):
758 Test failed TLS negotiation.
760 # Pretend that ssl is supported, it isn't actually used when the
761 # server starts out with a failure in response to our initial
762 # C{starttls} stanza.
765 d = self.init.start()
766 self.assertFailure(d, xmlstream.TLSFailed)
767 self.xmlstream.dataReceived("<failure xmlns='%s'/>" % NS_XMPP_TLS)
772 class TestFeatureInitializer(xmlstream.BaseFeatureInitiatingInitializer):
773 feature = ('testns', 'test')
776 return defer.succeed(None)
780 class BaseFeatureInitiatingInitializerTest(unittest.TestCase):
783 self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
784 self.init = TestFeatureInitializer(self.xmlstream)
787 def testAdvertized(self):
789 Test that an advertized feature results in successful initialization.
791 self.xmlstream.features = {self.init.feature:
792 domish.Element(self.init.feature)}
793 return self.init.initialize()
796 def testNotAdvertizedRequired(self):
798 Test that when the feature is not advertized, but required by the
799 initializer, an exception is raised.
801 self.init.required = True
802 self.assertRaises(xmlstream.FeatureNotAdvertized, self.init.initialize)
805 def testNotAdvertizedNotRequired(self):
807 Test that when the feature is not advertized, and not required by the
808 initializer, the initializer silently succeeds.
810 self.init.required = False
811 self.assertIdentical(None, self.init.initialize())
815 class ToResponseTest(unittest.TestCase):
817 def test_toResponse(self):
819 Test that a response stanza is generated with addressing swapped.
821 stanza = domish.Element(('jabber:client', 'iq'))
822 stanza['type'] = 'get'
823 stanza['to'] = 'user1@example.com'
824 stanza['from'] = 'user2@example.com/resource'
825 stanza['id'] = 'stanza1'
826 response = xmlstream.toResponse(stanza, 'result')
827 self.assertNotIdentical(stanza, response)
828 self.assertEqual(response['from'], 'user1@example.com')
829 self.assertEqual(response['to'], 'user2@example.com/resource')
830 self.assertEqual(response['type'], 'result')
831 self.assertEqual(response['id'], 'stanza1')
834 def test_toResponseNoFrom(self):
836 Test that a response is generated from a stanza without a from address.
838 stanza = domish.Element(('jabber:client', 'iq'))
839 stanza['type'] = 'get'
840 stanza['to'] = 'user1@example.com'
841 response = xmlstream.toResponse(stanza)
842 self.assertEqual(response['from'], 'user1@example.com')
843 self.assertFalse(response.hasAttribute('to'))
846 def test_toResponseNoTo(self):
848 Test that a response is generated from a stanza without a to address.
850 stanza = domish.Element(('jabber:client', 'iq'))
851 stanza['type'] = 'get'
852 stanza['from'] = 'user2@example.com/resource'
853 response = xmlstream.toResponse(stanza)
854 self.assertFalse(response.hasAttribute('from'))
855 self.assertEqual(response['to'], 'user2@example.com/resource')
858 def test_toResponseNoAddressing(self):
860 Test that a response is generated from a stanza without any addressing.
862 stanza = domish.Element(('jabber:client', 'message'))
863 stanza['type'] = 'chat'
864 response = xmlstream.toResponse(stanza)
865 self.assertFalse(response.hasAttribute('to'))
866 self.assertFalse(response.hasAttribute('from'))
871 Test that a proper response is generated without id attribute.
873 stanza = domish.Element(('jabber:client', 'message'))
874 response = xmlstream.toResponse(stanza)
875 self.assertFalse(response.hasAttribute('id'))
878 def test_noType(self):
880 Test that a proper response is generated without type attribute.
882 stanza = domish.Element(('jabber:client', 'message'))
883 response = xmlstream.toResponse(stanza)
884 self.assertFalse(response.hasAttribute('type'))
887 class DummyFactory(object):
889 Dummy XmlStream factory that only registers bootstrap observers.
895 def addBootstrap(self, event, callback):
896 self.callbacks[event] = callback
900 class DummyXMPPHandler(xmlstream.XMPPHandler):
902 Dummy XMPP subprotocol handler to count the methods are called on it.
906 self.doneInitialized = 0
910 def makeConnection(self, xs):
911 self.connectionMade()
914 def connectionMade(self):
918 def connectionInitialized(self):
919 self.doneInitialized += 1
922 def connectionLost(self, reason):
927 class FailureReasonXMPPHandler(xmlstream.XMPPHandler):
929 Dummy handler specifically for failure Reason tests.
932 self.gotFailureReason = False
935 def connectionLost(self, reason):
936 if isinstance(reason, failure.Failure):
937 self.gotFailureReason = True
941 class XMPPHandlerTest(unittest.TestCase):
943 Tests for L{xmlstream.XMPPHandler}.
946 def test_interface(self):
948 L{xmlstream.XMPPHandler} implements L{ijabber.IXMPPHandler}.
950 verifyObject(ijabber.IXMPPHandler, xmlstream.XMPPHandler())
955 Test that data is passed on for sending by the stream manager.
957 class DummyStreamManager(object):
961 def send(self, data):
962 self.outlist.append(data)
964 handler = xmlstream.XMPPHandler()
965 handler.parent = DummyStreamManager()
966 handler.send('<presence/>')
967 self.assertEqual(['<presence/>'], handler.parent.outlist)
970 def test_makeConnection(self):
972 Test that makeConnection saves the XML stream and calls connectionMade.
974 class TestXMPPHandler(xmlstream.XMPPHandler):
975 def connectionMade(self):
978 handler = TestXMPPHandler()
979 xs = xmlstream.XmlStream(xmlstream.Authenticator())
980 handler.makeConnection(xs)
981 self.assertTrue(handler.doneMade)
982 self.assertIdentical(xs, handler.xmlstream)
985 def test_connectionLost(self):
987 Test that connectionLost forgets the XML stream.
989 handler = xmlstream.XMPPHandler()
990 xs = xmlstream.XmlStream(xmlstream.Authenticator())
991 handler.makeConnection(xs)
992 handler.connectionLost(Exception())
993 self.assertIdentical(None, handler.xmlstream)
997 class XMPPHandlerCollectionTest(unittest.TestCase):
999 Tests for L{xmlstream.XMPPHandlerCollection}.
1003 self.collection = xmlstream.XMPPHandlerCollection()
1006 def test_interface(self):
1008 L{xmlstream.StreamManager} implements L{ijabber.IXMPPHandlerCollection}.
1010 verifyObject(ijabber.IXMPPHandlerCollection, self.collection)
1013 def test_addHandler(self):
1015 Test the addition of a protocol handler.
1017 handler = DummyXMPPHandler()
1018 handler.setHandlerParent(self.collection)
1019 self.assertIn(handler, self.collection)
1020 self.assertIdentical(self.collection, handler.parent)
1023 def test_removeHandler(self):
1025 Test removal of a protocol handler.
1027 handler = DummyXMPPHandler()
1028 handler.setHandlerParent(self.collection)
1029 handler.disownHandlerParent(self.collection)
1030 self.assertNotIn(handler, self.collection)
1031 self.assertIdentical(None, handler.parent)
1035 class StreamManagerTest(unittest.TestCase):
1037 Tests for L{xmlstream.StreamManager}.
1041 factory = DummyFactory()
1042 self.streamManager = xmlstream.StreamManager(factory)
1045 def test_basic(self):
1047 Test correct initialization and setup of factory observers.
1049 sm = self.streamManager
1050 self.assertIdentical(None, sm.xmlstream)
1051 self.assertEqual([], sm.handlers)
1052 self.assertEqual(sm._connected,
1053 sm.factory.callbacks['//event/stream/connected'])
1054 self.assertEqual(sm._authd,
1055 sm.factory.callbacks['//event/stream/authd'])
1056 self.assertEqual(sm._disconnected,
1057 sm.factory.callbacks['//event/stream/end'])
1058 self.assertEqual(sm.initializationFailed,
1059 sm.factory.callbacks['//event/xmpp/initfailed'])
1062 def test_connected(self):
1064 Test that protocol handlers have their connectionMade method called
1065 when the XML stream is connected.
1067 sm = self.streamManager
1068 handler = DummyXMPPHandler()
1069 handler.setHandlerParent(sm)
1070 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1072 self.assertEqual(1, handler.doneMade)
1073 self.assertEqual(0, handler.doneInitialized)
1074 self.assertEqual(0, handler.doneLost)
1077 def test_connectedLogTrafficFalse(self):
1079 Test raw data functions unset when logTraffic is set to False.
1081 sm = self.streamManager
1082 handler = DummyXMPPHandler()
1083 handler.setHandlerParent(sm)
1084 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1086 self.assertIdentical(None, xs.rawDataInFn)
1087 self.assertIdentical(None, xs.rawDataOutFn)
1090 def test_connectedLogTrafficTrue(self):
1092 Test raw data functions set when logTraffic is set to True.
1094 sm = self.streamManager
1095 sm.logTraffic = True
1096 handler = DummyXMPPHandler()
1097 handler.setHandlerParent(sm)
1098 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1100 self.assertNotIdentical(None, xs.rawDataInFn)
1101 self.assertNotIdentical(None, xs.rawDataOutFn)
1104 def test_authd(self):
1106 Test that protocol handlers have their connectionInitialized method
1107 called when the XML stream is initialized.
1109 sm = self.streamManager
1110 handler = DummyXMPPHandler()
1111 handler.setHandlerParent(sm)
1112 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1114 self.assertEqual(0, handler.doneMade)
1115 self.assertEqual(1, handler.doneInitialized)
1116 self.assertEqual(0, handler.doneLost)
1119 def test_disconnected(self):
1121 Test that protocol handlers have their connectionLost method
1122 called when the XML stream is disconnected.
1124 sm = self.streamManager
1125 handler = DummyXMPPHandler()
1126 handler.setHandlerParent(sm)
1127 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1128 sm._disconnected(xs)
1129 self.assertEqual(0, handler.doneMade)
1130 self.assertEqual(0, handler.doneInitialized)
1131 self.assertEqual(1, handler.doneLost)
1134 def test_disconnectedReason(self):
1136 A L{STREAM_END_EVENT} results in L{StreamManager} firing the handlers
1137 L{connectionLost} methods, passing a L{failure.Failure} reason.
1139 sm = self.streamManager
1140 handler = FailureReasonXMPPHandler()
1141 handler.setHandlerParent(sm)
1142 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1143 sm._disconnected(failure.Failure(Exception("no reason")))
1144 self.assertEqual(True, handler.gotFailureReason)
1147 def test_addHandler(self):
1149 Test the addition of a protocol handler while not connected.
1151 sm = self.streamManager
1152 handler = DummyXMPPHandler()
1153 handler.setHandlerParent(sm)
1155 self.assertEqual(0, handler.doneMade)
1156 self.assertEqual(0, handler.doneInitialized)
1157 self.assertEqual(0, handler.doneLost)
1160 def test_addHandlerInitialized(self):
1162 Test the addition of a protocol handler after the stream
1163 have been initialized.
1165 Make sure that the handler will have the connected stream
1166 passed via C{makeConnection} and have C{connectionInitialized}
1169 sm = self.streamManager
1170 xs = xmlstream.XmlStream(xmlstream.Authenticator())
1173 handler = DummyXMPPHandler()
1174 handler.setHandlerParent(sm)
1176 self.assertEqual(1, handler.doneMade)
1177 self.assertEqual(1, handler.doneInitialized)
1178 self.assertEqual(0, handler.doneLost)
1181 def test_sendInitialized(self):
1183 Test send when the stream has been initialized.
1185 The data should be sent directly over the XML stream.
1187 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
1188 sm = xmlstream.StreamManager(factory)
1189 xs = factory.buildProtocol(None)
1190 xs.transport = proto_helpers.StringTransport()
1192 xs.dataReceived("<stream:stream xmlns='jabber:client' "
1193 "xmlns:stream='http://etherx.jabber.org/streams' "
1194 "from='example.com' id='12345'>")
1195 xs.dispatch(xs, "//event/stream/authd")
1196 sm.send("<presence/>")
1197 self.assertEqual("<presence/>", xs.transport.value())
1200 def test_sendNotConnected(self):
1202 Test send when there is no established XML stream.
1204 The data should be cached until an XML stream has been established and
1207 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
1208 sm = xmlstream.StreamManager(factory)
1209 handler = DummyXMPPHandler()
1210 sm.addHandler(handler)
1212 xs = factory.buildProtocol(None)
1213 xs.transport = proto_helpers.StringTransport()
1214 sm.send("<presence/>")
1215 self.assertEqual("", xs.transport.value())
1216 self.assertEqual("<presence/>", sm._packetQueue[0])
1219 self.assertEqual("", xs.transport.value())
1220 self.assertEqual("<presence/>", sm._packetQueue[0])
1222 xs.dataReceived("<stream:stream xmlns='jabber:client' "
1223 "xmlns:stream='http://etherx.jabber.org/streams' "
1224 "from='example.com' id='12345'>")
1225 xs.dispatch(xs, "//event/stream/authd")
1227 self.assertEqual("<presence/>", xs.transport.value())
1228 self.assertFalse(sm._packetQueue)
1231 def test_sendNotInitialized(self):
1233 Test send when the stream is connected but not yet initialized.
1235 The data should be cached until the XML stream has been initialized.
1237 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
1238 sm = xmlstream.StreamManager(factory)
1239 xs = factory.buildProtocol(None)
1240 xs.transport = proto_helpers.StringTransport()
1242 xs.dataReceived("<stream:stream xmlns='jabber:client' "
1243 "xmlns:stream='http://etherx.jabber.org/streams' "
1244 "from='example.com' id='12345'>")
1245 sm.send("<presence/>")
1246 self.assertEqual("", xs.transport.value())
1247 self.assertEqual("<presence/>", sm._packetQueue[0])
1250 def test_sendDisconnected(self):
1252 Test send after XML stream disconnection.
1254 The data should be cached until a new XML stream has been established
1257 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
1258 sm = xmlstream.StreamManager(factory)
1259 handler = DummyXMPPHandler()
1260 sm.addHandler(handler)
1262 xs = factory.buildProtocol(None)
1264 xs.transport = proto_helpers.StringTransport()
1265 xs.connectionLost(None)
1267 sm.send("<presence/>")
1268 self.assertEqual("", xs.transport.value())
1269 self.assertEqual("<presence/>", sm._packetQueue[0])
1273 class XmlStreamServerFactoryTest(GenericXmlStreamFactoryTestsMixin):
1275 Tests for L{xmlstream.XmlStreamServerFactory}.
1280 Set up a server factory with a authenticator factory function.
1282 class TestAuthenticator(object):
1284 self.xmlstreams = []
1286 def associateWithStream(self, xs):
1287 self.xmlstreams.append(xs)
1289 def authenticatorFactory():
1290 return TestAuthenticator()
1292 self.factory = xmlstream.XmlStreamServerFactory(authenticatorFactory)
1295 def test_interface(self):
1297 L{XmlStreamServerFactory} is a L{Factory}.
1299 verifyObject(IProtocolFactory, self.factory)
1302 def test_buildProtocolAuthenticatorInstantiation(self):
1304 The authenticator factory should be used to instantiate the
1305 authenticator and pass it to the protocol.
1307 The default protocol, L{XmlStream} stores the authenticator it is
1308 passed, and calls its C{associateWithStream} method. so we use that to
1309 check whether our authenticator factory is used and the protocol
1310 instance gets an authenticator.
1312 xs = self.factory.buildProtocol(None)
1313 self.assertEqual([xs], xs.authenticator.xmlstreams)
1316 def test_buildProtocolXmlStream(self):
1318 The protocol factory creates Jabber XML Stream protocols by default.
1320 xs = self.factory.buildProtocol(None)
1321 self.assertIsInstance(xs, xmlstream.XmlStream)
1324 def test_buildProtocolTwice(self):
1326 Subsequent calls to buildProtocol should result in different instances
1327 of the protocol, as well as their authenticators.
1329 xs1 = self.factory.buildProtocol(None)
1330 xs2 = self.factory.buildProtocol(None)
1331 self.assertNotIdentical(xs1, xs2)
1332 self.assertNotIdentical(xs1.authenticator, xs2.authenticator)