diff --git a/tests/sleektest.py b/tests/sleektest.py
index eed52ec..eef3b90 100644
--- a/tests/sleektest.py
+++ b/tests/sleektest.py
@@ -7,11 +7,80 @@
"""
import unittest
+import socket
+try:
+ import queue
+except ImportError:
+ import Queue as queue
from xml.etree import cElementTree as ET
+from sleekxmpp import ClientXMPP
from sleekxmpp import Message, Iq
from sleekxmpp.stanza.presence import Presence
from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
+class TestSocket(object):
+
+ def __init__(self, *args, **kwargs):
+ self.socket = socket.socket(*args, **kwargs)
+ self.recv_queue = queue.Queue()
+ self.send_queue = queue.Queue()
+
+ def __getattr__(self, name):
+ """Pass requests through to actual socket"""
+ # Override a few methods to prevent actual socket connections
+ overrides = {'connect': lambda *args: None,
+ 'close': lambda *args: None,
+ 'shutdown': lambda *args: None}
+ return overrides.get(name, getattr(self.socket, name))
+
+ # ------------------------------------------------------------------
+ # Testing Interface
+
+ def nextSent(self, timeout=None):
+ """Get the next stanza that has been 'sent'"""
+ args = {'block': False}
+ if timeout is not None:
+ args = {'block': True, 'timeout': timeout}
+ try:
+ return self.send_queue.get(**args)
+ except:
+ return None
+
+ def recvData(self, data):
+ """Add data to the receiving queue"""
+ self.recv_queue.put(data)
+
+ # ------------------------------------------------------------------
+ # Socket Interface
+
+ def recv(self, *args, **kwargs):
+ return self.read(block=True)
+
+ def send(self, data):
+ self.send_queue.put(data)
+
+ # ------------------------------------------------------------------
+ # File Socket
+
+ def makefile(self, mode='r', bufsize=-1):
+ """File socket version to use with ElementTree"""
+ return self
+
+ def read(self, size=4096, block=True, timeout=None):
+ """Implement the file socket interface"""
+ if timeout is not None:
+ block = True
+ try:
+ return self.recv_queue.get(block, timeout)
+ except:
+ return None
+
+class TestStream(object):
+ """Dummy class to pass a stream object to created stanzas"""
+
+ def __init__(self):
+ self.default_ns = 'jabber:client'
+
class SleekTest(unittest.TestCase):
"""
@@ -27,6 +96,9 @@ class SleekTest(unittest.TestCase):
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
stanza.plugin_tag_map[tag] = plugin
+ # ------------------------------------------------------------------
+ # Shortcut methods for creating stanza objects
+
def Message(self, *args, **kwargs):
"""Create a message stanza."""
return Message(None, *args, **kwargs)
@@ -39,6 +111,9 @@ class SleekTest(unittest.TestCase):
"""Create a presence stanza."""
return Presence(None, *args, **kwargs)
+ # ------------------------------------------------------------------
+ # Methods for comparing stanza objects to XML strings
+
def checkMessage(self, msg, xml_string, use_values=True):
"""
Create and compare several message stanza objects to a
@@ -48,10 +123,12 @@ class SleekTest(unittest.TestCase):
setValues() will not be used.
"""
+ self.fix_namespaces(msg.xml, 'jabber:client')
debug = "Given Stanza:\n%s\n" % ET.tostring(msg.xml)
xml = ET.fromstring(xml_string)
- xml.tag = '{jabber:client}message'
+ self.fix_namespaces(xml, 'jabber:client')
+
debug += "XML String:\n%s\n" % ET.tostring(xml)
msg2 = self.Message(xml)
@@ -69,8 +146,7 @@ class SleekTest(unittest.TestCase):
debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml)
debug = "Three methods for creating stanza do not match:\n" + debug
- self.failUnless(self.compare([xml, msg.xml,
- msg2.xml, msg3.xml]),
+ self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]),
debug)
else:
debug = "Two methods for creating stanza do not match:\n" + debug
@@ -84,10 +160,12 @@ class SleekTest(unittest.TestCase):
If use_values is False, the test using getValues() and
setValues() will not be used.
"""
+
+ self.fix_namespaces(iq.xml, 'jabber:client')
debug = "Given Stanza:\n%s\n" % ET.tostring(iq.xml)
xml = ET.fromstring(xml_string)
- xml.tag = '{jabber:client}iq'
+ self.fix_namespaces(xml, 'jabber:client')
debug += "XML String:\n%s\n" % ET.tostring(xml)
iq2 = self.Iq(xml)
@@ -116,6 +194,69 @@ class SleekTest(unittest.TestCase):
"""
pass
+ # ------------------------------------------------------------------
+ # Methods for simulating stanza streams.
+
+ def streamStart(self, mode='client', skip=True):
+ if mode == 'client':
+ self.xmpp = ClientXMPP('tester@localhost', 'test')
+ self.xmpp.setSocket(TestSocket())
+
+ self.xmpp.state.set('reconnect', False)
+ self.xmpp.state.set('is client', True)
+ self.xmpp.state.set('connected', True)
+
+ # Must have the stream header ready for xmpp.process() to work
+ self.xmpp.socket.recvData(self.xmpp.stream_header)
+
+ self.xmpp.connectTCP = lambda a, b, c, d: True
+ self.xmpp.startTLS = lambda: True
+ self.xmpp.process(threaded=True)
+ if skip:
+ # Clear startup stanzas
+ self.xmpp.socket.nextSent(timeout=1)
+
+ def streamRecv(self, data):
+ data = str(data)
+ self.xmpp.socket.recvData(data)
+
+ def streamSendMessage(self, data, use_values=True, timeout=.5):
+ if isinstance(data, str):
+ data = self.Message(xml=ET.fromstring(data))
+ sent = self.xmpp.socket.nextSent(timeout=1)
+ self.checkMessage(data, sent, use_values)
+
+ def streamSendIq(self, data, use_values=True, timeout=.5):
+ if isinstance(data, str):
+ data = self.Iq(xml=ET.fromstring(data))
+ sent = self.xmpp.socket.nextSent(timeout)
+ self.checkIq(data, sent, use_values)
+
+ def streamSendPresence(self, data, use_values=True, timeout=.5):
+ if isinstance(data, str):
+ data = self.Presence(xml=ET.fromstring(data))
+ sent = self.xmpp.socket.nextSent(timeout)
+ self.checkPresence(data, sent, use_values)
+
+ def streamClose(self):
+ if self.xmpp is not None:
+ self.xmpp.disconnect()
+ self.xmpp.socket.recvData(self.xmpp.stream_footer)
+
+ # ------------------------------------------------------------------
+ # XML Comparison and Cleanup
+
+ def fix_namespaces(self, xml, ns):
+ """
+ Assign a namespace to an element and any children that
+ don't have a namespace.
+ """
+ if xml.tag.startswith('{'):
+ return
+ xml.tag = '{%s}%s' % (ns, xml.tag)
+ for child in xml.getchildren():
+ self.fix_namespaces(child, ns)
+
def compare(self, xml1, xml2=None):
"""
Compare XML objects.
@@ -137,7 +278,6 @@ class SleekTest(unittest.TestCase):
# Step 1: Check tags
if xml1.tag != xml2.tag:
- print xml1.tag, xml2.tag
return False
# Step 2: Check attributes
diff --git a/tests/test_stream.py b/tests/test_stream.py
new file mode 100644
index 0000000..eb4aaa5
--- /dev/null
+++ b/tests/test_stream.py
@@ -0,0 +1,34 @@
+from sleektest import *
+import sleekxmpp.plugins.xep_0033 as xep_0033
+
+
+class TestStreamTester(SleekTest):
+ """
+ Test that we can simulate and test a stanza stream.
+ """
+
+ def setUp(self):
+ self.streamStart()
+
+ def tearDown(self):
+ self.streamClose()
+
+ def testEcho(self):
+ def echo(msg):
+ msg.reply('Thanks for sending: %(body)s' % msg).send()
+
+ self.xmpp.add_event_handler('message', echo)
+
+ self.streamRecv("""
+
+ Hi!
+
+ """)
+
+ self.streamSendMessage("""
+
+ Thanks for sending: Hi!
+
+ """)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamTester)