summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/sleektest.py519
-rw-r--r--tests/test_addresses.py111
-rw-r--r--tests/test_chatstates.py44
-rw-r--r--tests/test_disco.py203
-rw-r--r--tests/test_elementbase.py193
-rw-r--r--tests/test_errorstanzas.py56
-rw-r--r--tests/test_events.py12
-rw-r--r--tests/test_forms.py115
-rw-r--r--tests/test_gmail.py88
-rw-r--r--tests/test_iqstanzas.py90
-rw-r--r--tests/test_jid.py26
-rw-r--r--tests/test_messagestanzas.py101
-rw-r--r--tests/test_presencestanzas.py98
-rw-r--r--tests/test_pubsubstanzas.py806
-rw-r--r--tests/test_roster.py84
-rw-r--r--tests/test_stream.py34
-rw-r--r--tests/test_tostring.py104
-rw-r--r--tests/xmlcompare.py28
18 files changed, 2205 insertions, 507 deletions
diff --git a/tests/sleektest.py b/tests/sleektest.py
new file mode 100644
index 00000000..801253d3
--- /dev/null
+++ b/tests/sleektest.py
@@ -0,0 +1,519 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import unittest
+import socket
+try:
+ import queue
+except ImportError:
+ import Queue as queue
+
+import sleekxmpp
+from sleekxmpp import ClientXMPP
+from sleekxmpp.stanza import Message, Iq, Presence
+from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin, ET
+from sleekxmpp.xmlstream.tostring import tostring
+
+
+class TestSocket(object):
+
+ """
+ A dummy socket that reads and writes to queues instead
+ of an actual networking socket.
+
+ Methods:
+ nextSent -- Return the next sent stanza.
+ recvData -- Make a stanza available to read next.
+ recv -- Read the next stanza from the socket.
+ send -- Write a stanza to the socket.
+ makefile -- Dummy call, returns self.
+ read -- Read the next stanza from the socket.
+ """
+
+ def __init__(self, *args, **kwargs):
+ """
+ Create a new test socket.
+
+ Arguments:
+ Same as arguments for socket.socket
+ """
+ self.socket = socket.socket(*args, **kwargs)
+ self.recv_queue = queue.Queue()
+ self.send_queue = queue.Queue()
+
+ def __getattr__(self, name):
+ """
+ Return attribute values of internal, dummy socket.
+
+ Some attributes and methods are disabled to prevent the
+ socket from connecting to the network.
+
+ Arguments:
+ name -- Name of the attribute requested.
+ """
+
+ def dummy(*args):
+ """Method to do nothing and prevent actual socket connections."""
+ return None
+
+ overrides = {'connect': dummy,
+ 'close': dummy,
+ 'shutdown': dummy}
+
+ return overrides.get(name, getattr(self.socket, name))
+
+ # ------------------------------------------------------------------
+ # Testing Interface
+
+ def nextSent(self, timeout=None):
+ """
+ Get the next stanza that has been 'sent'.
+
+ Arguments:
+ timeout -- Optional timeout for waiting for a new value.
+ """
+ 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.
+
+ Arguments:
+ data -- String data to 'write' to the socket to be received
+ by the XMPP client.
+ """
+ self.recv_queue.put(data)
+
+ # ------------------------------------------------------------------
+ # Socket Interface
+
+ def recv(self, *args, **kwargs):
+ """
+ Read a value from the received queue.
+
+ Arguments:
+ Placeholders. Same as for socket.Socket.recv.
+ """
+ return self.read(block=True)
+
+ def send(self, data):
+ """
+ Send data by placing it in the send queue.
+
+ Arguments:
+ data -- String value to write.
+ """
+ self.send_queue.put(data)
+
+ # ------------------------------------------------------------------
+ # File Socket
+
+ def makefile(self, *args, **kwargs):
+ """
+ File socket version to use with ElementTree.
+
+ Arguments:
+ Placeholders, same as socket.Socket.makefile()
+ """
+ return self
+
+ def read(self, block=True, timeout=None, **kwargs):
+ """
+ Implement the file socket interface.
+
+ Arguments:
+ block -- Indicate if the read should block until a
+ value is ready.
+ timeout -- Time in seconds a block should last before
+ returning None.
+ """
+ if timeout is not None:
+ block = True
+ try:
+ return self.recv_queue.get(block, timeout)
+ except:
+ return None
+
+
+class SleekTest(unittest.TestCase):
+
+ """
+ A SleekXMPP specific TestCase class that provides
+ methods for comparing message, iq, and presence stanzas.
+
+ Methods:
+ Message -- Create a Message stanza object.
+ Iq -- Create an Iq stanza object.
+ Presence -- Create a Presence stanza object.
+ checkMessage -- Compare a Message stanza against an XML string.
+ checkIq -- Compare an Iq stanza against an XML string.
+ checkPresence -- Compare a Presence stanza against an XML string.
+ streamStart -- Initialize a dummy XMPP client.
+ streamRecv -- Queue data for XMPP client to receive.
+ streamSendMessage -- Check that the XMPP client sent the given
+ Message stanza.
+ streamSendIq -- Check that the XMPP client sent the given
+ Iq stanza.
+ streamSendPresence -- Check taht the XMPP client sent the given
+ Presence stanza.
+ streamClose -- Disconnect the XMPP client.
+ fix_namespaces -- Add top-level namespace to an XML object.
+ compare -- Compare XML objects against each other.
+ """
+
+ # ------------------------------------------------------------------
+ # Shortcut methods for creating stanza objects
+
+ def Message(self, *args, **kwargs):
+ """
+ Create a Message stanza.
+
+ Uses same arguments as StanzaBase.__init__
+
+ Arguments:
+ xml -- An XML object to use for the Message's values.
+ """
+ return Message(None, *args, **kwargs)
+
+ def Iq(self, *args, **kwargs):
+ """
+ Create an Iq stanza.
+
+ Uses same arguments as StanzaBase.__init__
+
+ Arguments:
+ xml -- An XML object to use for the Iq's values.
+ """
+ return Iq(None, *args, **kwargs)
+
+ def Presence(self, *args, **kwargs):
+ """
+ Create a Presence stanza.
+
+ Uses same arguments as StanzaBase.__init__
+
+ Arguments:
+ xml -- An XML object to use for the Iq's values.
+ """
+ return Presence(None, *args, **kwargs)
+
+ # ------------------------------------------------------------------
+ # Methods for comparing stanza objects to XML strings
+
+ def checkStanza(self, stanza_class, stanza, xml_string,
+ defaults=None, use_values=True):
+ """
+ Create and compare several stanza objects to a correct XML string.
+
+ If use_values is False, test using getStanzaValues() and
+ setStanzaValues() will not be used.
+
+ Some stanzas provide default values for some interfaces, but
+ these defaults can be problematic for testing since they can easily
+ be forgotten when supplying the XML string. A list of interfaces that
+ use defaults may be provided and the generated stanzas will use the
+ default values for those interfaces if needed.
+
+ However, correcting the supplied XML is not possible for interfaces
+ that add or remove XML elements. Only interfaces that map to XML
+ attributes may be set using the defaults parameter. The supplied XML
+ must take into account any extra elements that are included by default.
+
+ Arguments:
+ stanza_class -- The class of the stanza being tested.
+ stanza -- The stanza object to test.
+ xml_string -- A string version of the correct XML expected.
+ defaults -- A list of stanza interfaces that have default
+ values. These interfaces will be set to their
+ defaults for the given and generated stanzas to
+ prevent unexpected test failures.
+ use_values -- Indicates if testing using getStanzaValues() and
+ setStanzaValues() should be used. Defaults to
+ True.
+ """
+ xml = ET.fromstring(xml_string)
+
+ # Ensure that top level namespaces are used, even if they
+ # were not provided.
+ self.fix_namespaces(stanza.xml, 'jabber:client')
+ self.fix_namespaces(xml, 'jabber:client')
+
+ stanza2 = stanza_class(xml=xml)
+
+ if use_values:
+ # Using getStanzaValues() and setStanzaValues() will add
+ # XML for any interface that has a default value. We need
+ # to set those defaults on the existing stanzas and XML
+ # so that they will compare correctly.
+ default_stanza = stanza_class()
+ if defaults is None:
+ defaults = []
+ for interface in defaults:
+ stanza[interface] = stanza[interface]
+ stanza2[interface] = stanza2[interface]
+ # Can really only automatically add defaults for top
+ # level attribute values. Anything else must be accounted
+ # for in the provided XML string.
+ if interface not in xml.attrib:
+ if interface in default_stanza.xml.attrib:
+ value = default_stanza.xml.attrib[interface]
+ xml.attrib[interface] = value
+
+ values = stanza2.getStanzaValues()
+ stanza3 = stanza_class()
+ stanza3.setStanzaValues(values)
+
+ debug = "Three methods for creating stanzas do not match.\n"
+ debug += "Given XML:\n%s\n" % tostring(xml)
+ debug += "Given stanza:\n%s\n" % tostring(stanza.xml)
+ debug += "Generated stanza:\n%s\n" % tostring(stanza2.xml)
+ debug += "Second generated stanza:\n%s\n" % tostring(stanza3.xml)
+ result = self.compare(xml, stanza.xml, stanza2.xml, stanza3.xml)
+ else:
+ debug = "Two methods for creating stanzas do not match.\n"
+ debug += "Given XML:\n%s\n" % tostring(xml)
+ debug += "Given stanza:\n%s\n" % tostring(stanza.xml)
+ debug += "Generated stanza:\n%s\n" % tostring(stanza2.xml)
+ result = self.compare(xml, stanza.xml, stanza2.xml)
+
+ self.failUnless(result, debug)
+
+ def checkMessage(self, msg, xml_string, use_values=True):
+ """
+ Create and compare several message stanza objects to a
+ correct XML string.
+
+ If use_values is False, the test using getStanzaValues() and
+ setStanzaValues() will not be used.
+
+ Arguments:
+ msg -- The Message stanza object to check.
+ xml_string -- The XML contents to compare against.
+ use_values -- Indicates if the test using getStanzaValues
+ and setStanzaValues should be used. Defaults
+ to True.
+ """
+
+ return self.checkStanza(Message, msg, xml_string,
+ defaults=['type'],
+ use_values = use_values)
+
+ def checkIq(self, iq, xml_string, use_values=True):
+ """
+ Create and compare several iq stanza objects to a
+ correct XML string.
+
+ If use_values is False, the test using getStanzaValues() and
+ setStanzaValues() will not be used.
+
+ Arguments:
+ iq -- The Iq stanza object to check.
+ xml_string -- The XML contents to compare against.
+ use_values -- Indicates if the test using getStanzaValues
+ and setStanzaValues should be used. Defaults
+ to True.
+ """
+ return self.checkStanza(Iq, iq, xml_string, use_values=use_values)
+
+ def checkPresence(self, pres, xml_string, use_values=True):
+ """
+ Create and compare several presence stanza objects to a
+ correct XML string.
+
+ If use_values is False, the test using getStanzaValues() and
+ setStanzaValues() will not be used.
+
+ Arguments:
+ iq -- The Iq stanza object to check.
+ xml_string -- The XML contents to compare against.
+ use_values -- Indicates if the test using getStanzaValues
+ and setStanzaValues should be used. Defaults
+ to True.
+ """
+ return self.checkStanza(Presence, pres, xml_string,
+ defaults=['priority'],
+ use_values=use_values)
+
+ # ------------------------------------------------------------------
+ # Methods for simulating stanza streams.
+
+ def streamStart(self, mode='client', skip=True):
+ """
+ Initialize an XMPP client or component using a dummy XML stream.
+
+ Arguments:
+ mode -- Either 'client' or 'component'. Defaults to 'client'.
+ skip -- Indicates if the first item in the sent queue (the
+ stream header) should be removed. Tests that wish
+ to test initializing the stream should set this to
+ False. Otherwise, the default of True should be used.
+ """
+ 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=0.01)
+
+ def streamRecv(self, data):
+ """
+ Pass data to the dummy XMPP client as if it came from an XMPP server.
+
+ Arguments:
+ data -- String stanza XML to be received and processed by the
+ XMPP client or component.
+ """
+ data = str(data)
+ self.xmpp.socket.recvData(data)
+
+ def streamSendMessage(self, data, use_values=True, timeout=.1):
+ """
+ Check that the XMPP client sent the given stanza XML.
+
+ Extracts the next sent stanza and compares it with the given
+ XML using checkMessage.
+
+ Arguments:
+ data -- The XML string of the expected Message stanza,
+ or an equivalent stanza object.
+ use_values -- Modifies the type of tests used by checkMessage.
+ timeout -- Time in seconds to wait for a stanza before
+ failing the check.
+ """
+ if isinstance(data, str):
+ data = self.Message(xml=ET.fromstring(data))
+ sent = self.xmpp.socket.nextSent(timeout)
+ self.checkMessage(data, sent, use_values)
+
+ def streamSendIq(self, data, use_values=True, timeout=.1):
+ """
+ Check that the XMPP client sent the given stanza XML.
+
+ Extracts the next sent stanza and compares it with the given
+ XML using checkIq.
+
+ Arguments:
+ data -- The XML string of the expected Iq stanza,
+ or an equivalent stanza object.
+ use_values -- Modifies the type of tests used by checkIq.
+ timeout -- Time in seconds to wait for a stanza before
+ failing the check.
+ """
+ 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=.1):
+ """
+ Check that the XMPP client sent the given stanza XML.
+
+ Extracts the next sent stanza and compares it with the given
+ XML using checkPresence.
+
+ Arguments:
+ data -- The XML string of the expected Presence stanza,
+ or an equivalent stanza object.
+ use_values -- Modifies the type of tests used by checkPresence.
+ timeout -- Time in seconds to wait for a stanza before
+ failing the check.
+ """
+ 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):
+ """
+ Disconnect the dummy XMPP client.
+
+ Can be safely called even if streamStart has not been called.
+
+ Must be placed in the tearDown method of a test class to ensure
+ that the XMPP client is disconnected after an error.
+ """
+ if hasattr(self, 'xmpp') and 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.
+
+ Arguments:
+ xml -- The XML object to fix.
+ ns -- The namespace to add to the XML object.
+ """
+ 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, xml, *other):
+ """
+ Compare XML objects.
+
+ Arguments:
+ xml -- The XML object to compare against.
+ *other -- The list of XML objects to compare.
+ """
+ if not other:
+ return False
+
+ # Compare multiple objects
+ if len(other) > 1:
+ for xml2 in other:
+ if not self.compare(xml, xml2):
+ return False
+ return True
+
+ other = other[0]
+
+ # Step 1: Check tags
+ if xml.tag != other.tag:
+ return False
+
+ # Step 2: Check attributes
+ if xml.attrib != other.attrib:
+ return False
+
+ # Step 3: Recursively check children
+ for child in xml:
+ child2s = other.findall("%s" % child.tag)
+ if child2s is None:
+ return False
+ for child2 in child2s:
+ if self.compare(child, child2):
+ break
+ else:
+ return False
+
+ # Everything matches
+ return True
diff --git a/tests/test_addresses.py b/tests/test_addresses.py
new file mode 100644
index 00000000..450e1362
--- /dev/null
+++ b/tests/test_addresses.py
@@ -0,0 +1,111 @@
+from . sleektest import *
+import sleekxmpp.plugins.xep_0033 as xep_0033
+
+
+class TestAddresses(SleekTest):
+
+ def setUp(self):
+ registerStanzaPlugin(Message, xep_0033.Addresses)
+
+ def testAddAddress(self):
+ """Testing adding extended stanza address."""
+ msg = self.Message()
+ msg['addresses'].addAddress(atype='to', jid='to@header1.org')
+ self.checkMessage(msg, """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address jid="to@header1.org" type="to" />
+ </addresses>
+ </message>
+ """)
+
+ msg = self.Message()
+ msg['addresses'].addAddress(atype='replyto',
+ jid='replyto@header1.org',
+ desc='Reply address')
+ self.checkMessage(msg, """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address jid="replyto@header1.org" type="replyto" desc="Reply address" />
+ </addresses>
+ </message>
+ """)
+
+ def testAddAddresses(self):
+ """Testing adding multiple extended stanza addresses."""
+
+ xmlstring = """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address jid="replyto@header1.org" type="replyto" desc="Reply address" />
+ <address jid="cc@header2.org" type="cc" />
+ <address jid="bcc@header2.org" type="bcc" />
+ </addresses>
+ </message>
+ """
+
+ msg = self.Message()
+ msg['addresses'].setAddresses([
+ {'type':'replyto',
+ 'jid':'replyto@header1.org',
+ 'desc':'Reply address'},
+ {'type':'cc',
+ 'jid':'cc@header2.org'},
+ {'type':'bcc',
+ 'jid':'bcc@header2.org'}])
+ self.checkMessage(msg, xmlstring)
+
+ msg = self.Message()
+ msg['addresses']['replyto'] = [{'jid':'replyto@header1.org',
+ 'desc':'Reply address'}]
+ msg['addresses']['cc'] = [{'jid':'cc@header2.org'}]
+ msg['addresses']['bcc'] = [{'jid':'bcc@header2.org'}]
+ self.checkMessage(msg, xmlstring)
+
+ def testAddURI(self):
+ """Testing adding URI attribute to extended stanza address."""
+
+ msg = self.Message()
+ addr = msg['addresses'].addAddress(atype='to',
+ jid='to@header1.org',
+ node='foo')
+ self.checkMessage(msg, """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address node="foo" jid="to@header1.org" type="to" />
+ </addresses>
+ </message>
+ """)
+
+ addr['uri'] = 'mailto:to@header2.org'
+ self.checkMessage(msg, """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address type="to" uri="mailto:to@header2.org" />
+ </addresses>
+ </message>
+ """)
+
+ def testDelivered(self):
+ """Testing delivered attribute of extended stanza addresses."""
+
+ xmlstring = """
+ <message>
+ <addresses xmlns="http://jabber.org/protocol/address">
+ <address %s jid="to@header1.org" type="to" />
+ </addresses>
+ </message>
+ """
+
+ msg = self.Message()
+ addr = msg['addresses'].addAddress(jid='to@header1.org', atype='to')
+ self.checkMessage(msg, xmlstring % '')
+
+ addr['delivered'] = True
+ self.checkMessage(msg, xmlstring % 'delivered="true"')
+
+ addr['delivered'] = False
+ self.checkMessage(msg, xmlstring % '')
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestAddresses)
diff --git a/tests/test_chatstates.py b/tests/test_chatstates.py
new file mode 100644
index 00000000..74359df9
--- /dev/null
+++ b/tests/test_chatstates.py
@@ -0,0 +1,44 @@
+from . sleektest import *
+import sleekxmpp.plugins.xep_0085 as xep_0085
+
+class TestChatStates(SleekTest):
+
+ def setUp(self):
+ registerStanzaPlugin(Message, xep_0085.Active)
+ registerStanzaPlugin(Message, xep_0085.Composing)
+ registerStanzaPlugin(Message, xep_0085.Gone)
+ registerStanzaPlugin(Message, xep_0085.Inactive)
+ registerStanzaPlugin(Message, xep_0085.Paused)
+
+ def testCreateChatState(self):
+ """Testing creating chat states."""
+
+ xmlstring = """
+ <message>
+ <%s xmlns="http://jabber.org/protocol/chatstates" />
+ </message>
+ """
+
+ msg = self.Message()
+ msg['chat_state'].active()
+ self.checkMessage(msg, xmlstring % 'active',
+ use_values=False)
+
+ msg['chat_state'].composing()
+ self.checkMessage(msg, xmlstring % 'composing',
+ use_values=False)
+
+
+ msg['chat_state'].gone()
+ self.checkMessage(msg, xmlstring % 'gone',
+ use_values=False)
+
+ msg['chat_state'].inactive()
+ self.checkMessage(msg, xmlstring % 'inactive',
+ use_values=False)
+
+ msg['chat_state'].paused()
+ self.checkMessage(msg, xmlstring % 'paused',
+ use_values=False)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestChatStates)
diff --git a/tests/test_disco.py b/tests/test_disco.py
index bbe285a6..2cc50ee0 100644
--- a/tests/test_disco.py
+++ b/tests/test_disco.py
@@ -1,155 +1,176 @@
-import unittest
-from xml.etree import cElementTree as ET
-from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
-from . import xmlcompare
+from . sleektest import *
+import sleekxmpp.plugins.xep_0030 as xep_0030
-import sleekxmpp.plugins.xep_0030 as sd
-def stanzaPlugin(stanza, plugin):
- stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
- stanza.plugin_tag_map["{%s}%s" % (plugin.namespace, plugin.name)] = plugin
-
-class testdisco(unittest.TestCase):
+class TestDisco(SleekTest):
def setUp(self):
- self.sd = sd
- stanzaPlugin(self.sd.Iq, self.sd.DiscoInfo)
- stanzaPlugin(self.sd.Iq, self.sd.DiscoItems)
-
- def try3Methods(self, xmlstring, iq):
- iq2 = self.sd.Iq(None, self.sd.ET.fromstring(xmlstring))
- values = iq2.getValues()
- iq3 = self.sd.Iq()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3), str(iq)+"3 methods for creating stanza don't match")
-
+ registerStanzaPlugin(Iq, xep_0030.DiscoInfo)
+ registerStanzaPlugin(Iq, xep_0030.DiscoItems)
+
def testCreateInfoQueryNoNode(self):
"""Testing disco#info query with no node."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_info']['node'] = ''
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#info" /></iq>"""
- self.try3Methods(xmlstring, iq)
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#info" />
+ </iq>
+ """)
def testCreateInfoQueryWithNode(self):
"""Testing disco#info query with a node."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_info']['node'] = 'foo'
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#info" node="foo" /></iq>"""
- self.try3Methods(xmlstring, iq)
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#info" node="foo" />
+ </iq>
+ """)
def testCreateInfoQueryNoNode(self):
"""Testing disco#items query with no node."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_items']['node'] = ''
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#items" /></iq>"""
- self.try3Methods(xmlstring, iq)
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#items" />
+ </iq>
+ """)
def testCreateItemsQueryWithNode(self):
"""Testing disco#items query with a node."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_items']['node'] = 'foo'
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#items" node="foo" /></iq>"""
- self.try3Methods(xmlstring, iq)
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#items" node="foo" />
+ </iq>
+ """)
def testInfoIdentities(self):
"""Testing adding identities to disco#info."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_info']['node'] = 'foo'
- iq['disco_info'].addIdentity('conference', 'text', 'Chatroom')
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#info" node="foo"><identity category="conference" type="text" name="Chatroom" /></query></iq>"""
- self.try3Methods(xmlstring, iq)
+ iq['disco_info'].addIdentity('conference', 'text', 'Chatroom')
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#info" node="foo">
+ <identity category="conference" type="text" name="Chatroom" />
+ </query>
+ </iq>
+ """)
def testInfoFeatures(self):
"""Testing adding features to disco#info."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_info']['node'] = 'foo'
- iq['disco_info'].addFeature('foo')
- iq['disco_info'].addFeature('bar')
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#info" node="foo"><feature var="foo" /><feature var="bar" /></query></iq>"""
- self.try3Methods(xmlstring, iq)
+ iq['disco_info'].addFeature('foo')
+ iq['disco_info'].addFeature('bar')
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#info" node="foo">
+ <feature var="foo" />
+ <feature var="bar" />
+ </query>
+ </iq>
+ """)
def testItems(self):
"""Testing adding features to disco#info."""
- iq = self.sd.Iq()
+ iq = self.Iq()
iq['id'] = "0"
iq['disco_items']['node'] = 'foo'
- iq['disco_items'].addItem('user@localhost')
- iq['disco_items'].addItem('user@localhost', 'foo')
- iq['disco_items'].addItem('user@localhost', 'bar', 'Testing')
- xmlstring = """<iq id="0"><query xmlns="http://jabber.org/protocol/disco#items" node="foo"><item jid="user@localhost" /><item node="foo" jid="user@localhost" /><item node="bar" jid="user@localhost" name="Testing" /></query></iq>"""
- self.try3Methods(xmlstring, iq)
+ iq['disco_items'].addItem('user@localhost')
+ iq['disco_items'].addItem('user@localhost', 'foo')
+ iq['disco_items'].addItem('user@localhost', 'bar', 'Testing')
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="http://jabber.org/protocol/disco#items" node="foo">
+ <item jid="user@localhost" />
+ <item node="foo" jid="user@localhost" />
+ <item node="bar" jid="user@localhost" name="Testing" />
+ </query>
+ </iq>
+ """)
def testAddRemoveIdentities(self):
"""Test adding and removing identities to disco#info stanza"""
- ids = [('automation', 'commands', 'AdHoc'),
- ('conference', 'text', 'ChatRoom')]
+ ids = [('automation', 'commands', 'AdHoc'),
+ ('conference', 'text', 'ChatRoom')]
- info = self.sd.DiscoInfo()
- info.addIdentity(*ids[0])
- self.failUnless(info.getIdentities() == [ids[0]])
+ info = xep_0030.DiscoInfo()
+ info.addIdentity(*ids[0])
+ self.failUnless(info.getIdentities() == [ids[0]])
- info.delIdentity('automation', 'commands')
- self.failUnless(info.getIdentities() == [])
+ info.delIdentity('automation', 'commands')
+ self.failUnless(info.getIdentities() == [])
- info.setIdentities(ids)
- self.failUnless(info.getIdentities() == ids)
+ info.setIdentities(ids)
+ self.failUnless(info.getIdentities() == ids)
- info.delIdentity('automation', 'commands')
- self.failUnless(info.getIdentities() == [ids[1]])
+ info.delIdentity('automation', 'commands')
+ self.failUnless(info.getIdentities() == [ids[1]])
- info.delIdentities()
- self.failUnless(info.getIdentities() == [])
+ info.delIdentities()
+ self.failUnless(info.getIdentities() == [])
def testAddRemoveFeatures(self):
"""Test adding and removing features to disco#info stanza"""
- features = ['foo', 'bar', 'baz']
+ features = ['foo', 'bar', 'baz']
- info = self.sd.DiscoInfo()
- info.addFeature(features[0])
- self.failUnless(info.getFeatures() == [features[0]])
+ info = xep_0030.DiscoInfo()
+ info.addFeature(features[0])
+ self.failUnless(info.getFeatures() == [features[0]])
- info.delFeature('foo')
- self.failUnless(info.getFeatures() == [])
+ info.delFeature('foo')
+ self.failUnless(info.getFeatures() == [])
- info.setFeatures(features)
- self.failUnless(info.getFeatures() == features)
+ info.setFeatures(features)
+ self.failUnless(info.getFeatures() == features)
- info.delFeature('bar')
- self.failUnless(info.getFeatures() == ['foo', 'baz'])
+ info.delFeature('bar')
+ self.failUnless(info.getFeatures() == ['foo', 'baz'])
- info.delFeatures()
- self.failUnless(info.getFeatures() == [])
+ info.delFeatures()
+ self.failUnless(info.getFeatures() == [])
def testAddRemoveItems(self):
"""Test adding and removing items to disco#items stanza"""
- items = [('user@localhost', None, None),
- ('user@localhost', 'foo', None),
- ('user@localhost', 'bar', 'Test')]
+ items = [('user@localhost', None, None),
+ ('user@localhost', 'foo', None),
+ ('user@localhost', 'bar', 'Test')]
+
+ info = xep_0030.DiscoItems()
+ self.failUnless(True, ""+str(items[0]))
- info = self.sd.DiscoItems()
- self.failUnless(True, ""+str(items[0]))
+ info.addItem(*(items[0]))
+ self.failUnless(info.getItems() == [items[0]], info.getItems())
- info.addItem(*(items[0]))
- self.failUnless(info.getItems() == [items[0]], info.getItems())
+ info.delItem('user@localhost')
+ self.failUnless(info.getItems() == [])
- info.delItem('user@localhost')
- self.failUnless(info.getItems() == [])
+ info.setItems(items)
+ self.failUnless(info.getItems() == items)
- info.setItems(items)
- self.failUnless(info.getItems() == items)
+ info.delItem('user@localhost', 'foo')
+ self.failUnless(info.getItems() == [items[0], items[2]])
- info.delItem('user@localhost', 'foo')
- self.failUnless(info.getItems() == [items[0], items[2]])
+ info.delItems()
+ self.failUnless(info.getItems() == [])
- info.delItems()
- self.failUnless(info.getItems() == [])
-
-
-suite = unittest.TestLoader().loadTestsFromTestCase(testdisco)
+suite = unittest.TestLoader().loadTestsFromTestCase(TestDisco)
diff --git a/tests/test_elementbase.py b/tests/test_elementbase.py
new file mode 100644
index 00000000..95502f54
--- /dev/null
+++ b/tests/test_elementbase.py
@@ -0,0 +1,193 @@
+from . sleektest import *
+from sleekxmpp.xmlstream.stanzabase import ElementBase
+
+class TestElementBase(SleekTest):
+
+ def testExtendedName(self):
+ """Test element names of the form tag1/tag2/tag3."""
+
+ class TestStanza(ElementBase):
+ name = "foo/bar/baz"
+ namespace = "test"
+
+ stanza = TestStanza()
+ self.checkStanza(TestStanza, stanza, """
+ <foo xmlns="test">
+ <bar>
+ <baz />
+ </bar>
+ </foo>
+ """)
+
+ def testGetStanzaValues(self):
+ """Test getStanzaValues using plugins and substanzas."""
+
+ class TestStanzaPlugin(ElementBase):
+ name = "foo2"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+ plugin_attrib = "foo2"
+
+ class TestSubStanza(ElementBase):
+ name = "subfoo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+
+ class TestStanza(ElementBase):
+ name = "foo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+ subitem = set((TestSubStanza,))
+
+ registerStanzaPlugin(TestStanza, TestStanzaPlugin)
+
+ stanza = TestStanza()
+ stanza['bar'] = 'a'
+ stanza['foo2']['baz'] = 'b'
+ substanza = TestSubStanza()
+ substanza['bar'] = 'c'
+ stanza.append(substanza)
+
+ values = stanza.getStanzaValues()
+ expected = {'bar': 'a',
+ 'baz': '',
+ 'foo2': {'bar': '',
+ 'baz': 'b'},
+ 'substanzas': [{'__childtag__': '{foo}subfoo',
+ 'bar': 'c',
+ 'baz': ''}]}
+ self.failUnless(values == expected,
+ "Unexpected stanza values:\n%s\n%s" % (str(expected), str(values)))
+
+
+ def testSetStanzaValues(self):
+ """Test using setStanzaValues with substanzas and plugins."""
+
+ class TestStanzaPlugin(ElementBase):
+ name = "pluginfoo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+ plugin_attrib = "plugin_foo"
+
+ class TestStanzaPlugin2(ElementBase):
+ name = "pluginfoo2"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+ plugin_attrib = "plugin_foo2"
+
+ class TestSubStanza(ElementBase):
+ name = "subfoo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+
+ class TestStanza(ElementBase):
+ name = "foo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz'))
+ subitem = set((TestSubStanza,))
+
+ registerStanzaPlugin(TestStanza, TestStanzaPlugin)
+ registerStanzaPlugin(TestStanza, TestStanzaPlugin2)
+
+ stanza = TestStanza()
+ values = {'bar': 'a',
+ 'baz': '',
+ 'plugin_foo': {'bar': '',
+ 'baz': 'b'},
+ 'plugin_foo2': {'bar': 'd',
+ 'baz': 'e'},
+ 'substanzas': [{'__childtag__': '{foo}subfoo',
+ 'bar': 'c',
+ 'baz': ''}]}
+ stanza.setStanzaValues(values)
+
+ self.checkStanza(TestStanza, stanza, """
+ <foo xmlns="foo" bar="a">
+ <pluginfoo baz="b" />
+ <pluginfoo2 bar="d" baz="e" />
+ <subfoo bar="c" />
+ </foo>
+ """)
+
+ def testGetItem(self):
+ """Test accessing stanza interfaces."""
+
+ class TestStanza(ElementBase):
+ name = "foo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz', 'qux'))
+ sub_interfaces = set(('baz',))
+
+ def getQux(self):
+ return 'qux'
+
+ class TestStanzaPlugin(ElementBase):
+ name = "foobar"
+ namespace = "foo"
+ plugin_attrib = "foobar"
+ interfaces = set(('fizz',))
+
+ TestStanza.subitem = (TestStanza,)
+ registerStanzaPlugin(TestStanza, TestStanzaPlugin)
+
+ stanza = TestStanza()
+ substanza = TestStanza()
+ stanza.append(substanza)
+ stanza.setStanzaValues({'bar': 'a',
+ 'baz': 'b',
+ 'qux': 42,
+ 'foobar': {'fizz': 'c'}})
+
+ # Test non-plugin interfaces
+ expected = {'substanzas': [substanza],
+ 'bar': 'a',
+ 'baz': 'b',
+ 'qux': 'qux',
+ 'meh': ''}
+ for interface, value in expected.items():
+ result = stanza[interface]
+ self.failUnless(result == value,
+ "Incorrect stanza interface access result: %s" % result)
+
+ # Test plugin interfaces
+ self.failUnless(isinstance(stanza['foobar'], TestStanzaPlugin),
+ "Incorrect plugin object result.")
+ self.failUnless(stanza['foobar']['fizz'] == 'c',
+ "Incorrect plugin subvalue result.")
+
+ def testSetItem(self):
+ """Test assigning to stanza interfaces."""
+
+ class TestStanza(ElementBase):
+ name = "foo"
+ namespace = "foo"
+ interfaces = set(('bar', 'baz', 'qux'))
+ sub_interfaces = set(('baz',))
+
+ def setQux(self, value):
+ pass
+
+ class TestStanzaPlugin(ElementBase):
+ name = "foobar"
+ namespace = "foo"
+ plugin_attrib = "foobar"
+ interfaces = set(('foobar',))
+
+ registerStanzaPlugin(TestStanza, TestStanzaPlugin)
+
+ stanza = TestStanza()
+
+ stanza['bar'] = 'attribute!'
+ stanza['baz'] = 'element!'
+ stanza['qux'] = 'overridden'
+ stanza['foobar'] = 'plugin'
+
+ self.checkStanza(TestStanza, stanza, """
+ <foo xmlns="foo" bar="attribute!">
+ <baz>element!</baz>
+ <foobar foobar="plugin" />
+ </foo>
+ """)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestElementBase)
diff --git a/tests/test_errorstanzas.py b/tests/test_errorstanzas.py
new file mode 100644
index 00000000..d6fafc59
--- /dev/null
+++ b/tests/test_errorstanzas.py
@@ -0,0 +1,56 @@
+from . sleektest import *
+
+class TestErrorStanzas(SleekTest):
+
+ def testSetup(self):
+ """Test setting initial values in error stanza."""
+ msg = self.Message()
+ msg.enable('error')
+ self.checkMessage(msg, """
+ <message type="error">
+ <error type="cancel">
+ <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
+ </error>
+ </message>
+ """)
+
+ def testCondition(self):
+ """Test modifying the error condition."""
+ msg = self.Message()
+ msg['error']['condition'] = 'item-not-found'
+
+ self.checkMessage(msg, """
+ <message type="error">
+ <error type="cancel">
+ <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
+ </error>
+ </message>
+ """)
+
+ self.failUnless(msg['error']['condition'] == 'item-not-found', "Error condition doesn't match.")
+
+ del msg['error']['condition']
+
+ self.checkMessage(msg, """
+ <message type="error">
+ <error type="cancel" />
+ </message>
+ """)
+
+ def testDelCondition(self):
+ """Test that deleting error conditions doesn't remove extra elements."""
+ msg = self.Message()
+ msg['error']['text'] = 'Error!'
+ msg['error']['condition'] = 'internal-server-error'
+
+ del msg['error']['condition']
+
+ self.checkMessage(msg, """
+ <message type="error">
+ <error type="cancel">
+ <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Error!</text>
+ </error>
+ </message>
+ """)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestErrorStanzas)
diff --git a/tests/test_events.py b/tests/test_events.py
index 11821dbb..bbc5832e 100644
--- a/tests/test_events.py
+++ b/tests/test_events.py
@@ -1,14 +1,11 @@
-import unittest
+import sleekxmpp
+from . sleektest import *
-class testevents(unittest.TestCase):
- def setUp(self):
- import sleekxmpp.stanza.presence as p
- self.p = p
+class TestEvents(SleekTest):
def testEventHappening(self):
"Test handler working"
- import sleekxmpp
c = sleekxmpp.ClientXMPP('crap@wherever', 'password')
happened = []
def handletestevent(event):
@@ -20,7 +17,6 @@ class testevents(unittest.TestCase):
def testDelEvent(self):
"Test handler working, then deleted and not triggered"
- import sleekxmpp
c = sleekxmpp.ClientXMPP('crap@wherever', 'password')
happened = []
def handletestevent(event):
@@ -32,4 +28,4 @@ class testevents(unittest.TestCase):
self.failUnless(happened == [True], "event did not get triggered the correct number of times")
-suite = unittest.TestLoader().loadTestsFromTestCase(testevents)
+suite = unittest.TestLoader().loadTestsFromTestCase(TestEvents)
diff --git a/tests/test_forms.py b/tests/test_forms.py
new file mode 100644
index 00000000..d5710633
--- /dev/null
+++ b/tests/test_forms.py
@@ -0,0 +1,115 @@
+from . sleektest import *
+import sleekxmpp.plugins.xep_0004 as xep_0004
+
+
+class TestDataForms(SleekTest):
+
+ def setUp(self):
+ registerStanzaPlugin(Message, xep_0004.Form)
+ registerStanzaPlugin(xep_0004.Form, xep_0004.FormField)
+ registerStanzaPlugin(xep_0004.FormField, xep_0004.FieldOption)
+
+ def testMultipleInstructions(self):
+ """Testing using multiple instructions elements in a data form."""
+ msg = self.Message()
+ msg['form']['instructions'] = "Instructions\nSecond batch"
+
+ self.checkMessage(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <instructions>Instructions</instructions>
+ <instructions>Second batch</instructions>
+ </x>
+ </message>
+ """)
+
+ def testAddField(self):
+ """Testing adding fields to a data form."""
+
+ msg = self.Message()
+ form = msg['form']
+ form.addField(var='f1',
+ ftype='text-single',
+ label='Text',
+ desc='A text field',
+ required=True,
+ value='Some text!')
+
+ self.checkMessage(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Text">
+ <desc>A text field</desc>
+ <required />
+ <value>Some text!</value>
+ </field>
+ </x>
+ </message>
+ """)
+
+ form['fields'] = [('f1', {'type': 'text-single',
+ 'label': 'Username',
+ 'required': True}),
+ ('f2', {'type': 'text-private',
+ 'label': 'Password',
+ 'required': True}),
+ ('f3', {'type': 'text-multi',
+ 'label': 'Message',
+ 'value': 'Enter message.\nA long one even.'}),
+ ('f4', {'type': 'list-single',
+ 'label': 'Message Type',
+ 'options': [{'label': 'Cool!',
+ 'value': 'cool'},
+ {'label': 'Urgh!',
+ 'value': 'urgh'}]})]
+ self.checkMessage(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="f1" type="text-single" label="Username">
+ <required />
+ </field>
+ <field var="f2" type="text-private" label="Password">
+ <required />
+ </field>
+ <field var="f3" type="text-multi" label="Message">
+ <value>Enter message.</value>
+ <value>A long one even.</value>
+ </field>
+ <field var="f4" type="list-single" label="Message Type">
+ <option label="Cool!">
+ <value>cool</value>
+ </option>
+ <option label="Urgh!">
+ <value>urgh</value>
+ </option>
+ </field>
+ </x>
+ </message>
+ """)
+
+ def testSetValues(self):
+ """Testing setting form values"""
+
+ msg = self.Message()
+ form = msg['form']
+ form.setFields([
+ ('foo', {'type': 'text-single'}),
+ ('bar', {'type': 'list-multi'})])
+
+ form.setValues({'foo': 'Foo!',
+ 'bar': ['a', 'b']})
+
+ self.checkMessage(msg, """
+ <message>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="foo" type="text-single">
+ <value>Foo!</value>
+ </field>
+ <field var="bar" type="list-multi">
+ <value>a</value>
+ <value>b</value>
+ </field>
+ </x>
+ </message>""")
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestDataForms)
diff --git a/tests/test_gmail.py b/tests/test_gmail.py
new file mode 100644
index 00000000..dd256e27
--- /dev/null
+++ b/tests/test_gmail.py
@@ -0,0 +1,88 @@
+from . sleektest import *
+import sleekxmpp.plugins.gmail_notify as gmail
+
+
+class TestGmail(SleekTest):
+
+ def setUp(self):
+ registerStanzaPlugin(Iq, gmail.GmailQuery)
+ registerStanzaPlugin(Iq, gmail.MailBox)
+ registerStanzaPlugin(Iq, gmail.NewMail)
+
+ def testCreateQuery(self):
+ """Testing querying Gmail for emails."""
+
+ iq = self.Iq()
+ iq['type'] = 'get'
+ iq['gmail']['search'] = 'is:starred'
+ iq['gmail']['newer-than-time'] = '1140638252542'
+ iq['gmail']['newer-than-tid'] = '11134623426430234'
+
+ self.checkIq(iq, """
+ <iq type="get">
+ <query xmlns="google:mail:notify"
+ newer-than-time="1140638252542"
+ newer-than-tid="11134623426430234"
+ q="is:starred" />
+ </iq>
+ """)
+
+ def testMailBox(self):
+ """Testing reading from Gmail mailbox result"""
+
+ # Use the example from Google's documentation at
+ # http://code.google.com/apis/talk/jep_extensions/gmail.html#notifications
+ xml = ET.fromstring("""
+ <iq type="result">
+ <mailbox xmlns="google:mail:notify"
+ result-time='1118012394209'
+ url='http://mail.google.com/mail'
+ total-matched='95'
+ total-estimate='0'>
+ <mail-thread-info tid='1172320964060972012'
+ participation='1'
+ messages='28'
+ date='1118012394209'
+ url='http://mail.google.com/mail?view=cv'>
+ <senders>
+ <sender name='Me' address='romeo@gmail.com' originator='1' />
+ <sender name='Benvolio' address='benvolio@gmail.com' />
+ <sender name='Mercutio' address='mercutio@gmail.com' unread='1'/>
+ </senders>
+ <labels>act1scene3</labels>
+ <subject>Put thy rapier up.</subject>
+ <snippet>Ay, ay, a scratch, a scratch; marry, 'tis enough.</snippet>
+ </mail-thread-info>
+ </mailbox>
+ </iq>
+ """)
+
+ iq = self.Iq(xml=xml)
+ mailbox = iq['mailbox']
+ self.failUnless(mailbox['result-time'] == '1118012394209', "result-time doesn't match")
+ self.failUnless(mailbox['url'] == 'http://mail.google.com/mail', "url doesn't match")
+ self.failUnless(mailbox['matched'] == '95', "total-matched incorrect")
+ self.failUnless(mailbox['estimate'] == False, "total-estimate incorrect")
+ self.failUnless(len(mailbox['threads']) == 1, "could not extract message threads")
+
+ thread = mailbox['threads'][0]
+ self.failUnless(thread['tid'] == '1172320964060972012', "thread tid doesn't match")
+ self.failUnless(thread['participation'] == '1', "thread participation incorrect")
+ self.failUnless(thread['messages'] == '28', "thread message count incorrect")
+ self.failUnless(thread['date'] == '1118012394209', "thread date doesn't match")
+ self.failUnless(thread['url'] == 'http://mail.google.com/mail?view=cv', "thread url doesn't match")
+ self.failUnless(thread['labels'] == 'act1scene3', "thread labels incorrect")
+ self.failUnless(thread['subject'] == 'Put thy rapier up.', "thread subject doesn't match")
+ self.failUnless(thread['snippet'] == "Ay, ay, a scratch, a scratch; marry, 'tis enough.", "snippet doesn't match")
+ self.failUnless(len(thread['senders']) == 3, "could not extract senders")
+
+ sender1 = thread['senders'][0]
+ self.failUnless(sender1['name'] == 'Me', "sender name doesn't match")
+ self.failUnless(sender1['address'] == 'romeo@gmail.com', "sender address doesn't match")
+ self.failUnless(sender1['originator'] == True, "sender originator incorrect")
+ self.failUnless(sender1['unread'] == False, "sender unread incorrectly True")
+
+ sender2 = thread['senders'][2]
+ self.failUnless(sender2['unread'] == True, "sender unread incorrectly False")
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestGmail)
diff --git a/tests/test_iqstanzas.py b/tests/test_iqstanzas.py
new file mode 100644
index 00000000..2dabc5e9
--- /dev/null
+++ b/tests/test_iqstanzas.py
@@ -0,0 +1,90 @@
+from . sleektest import *
+from sleekxmpp.xmlstream.stanzabase import ET
+
+
+class TestIqStanzas(SleekTest):
+
+ def tearDown(self):
+ """Shutdown the XML stream after testing."""
+ self.streamClose()
+
+ def testSetup(self):
+ """Test initializing default Iq values."""
+ iq = self.Iq()
+ self.checkIq(iq, """
+ <iq id="0" />
+ """)
+
+ def testPayload(self):
+ """Test setting Iq stanza payload."""
+ iq = self.Iq()
+ iq.setPayload(ET.Element('{test}tester'))
+ self.checkIq(iq, """
+ <iq id="0">
+ <tester xmlns="test" />
+ </iq>
+ """, use_values=False)
+
+
+ def testUnhandled(self):
+ """Test behavior for Iq.unhandled."""
+ self.streamStart()
+ self.streamRecv("""
+ <iq id="test" type="get">
+ <query xmlns="test" />
+ </iq>
+ """)
+
+ iq = self.Iq()
+ iq['id'] = 'test'
+ iq['error']['condition'] = 'feature-not-implemented'
+ iq['error']['text'] = 'No handlers registered for this request.'
+
+ self.streamSendIq(iq, """
+ <iq id="test" type="error">
+ <error type="cancel">
+ <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
+ <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
+ No handlers registered for this request.
+ </text>
+ </error>
+ </iq>
+ """)
+
+ def testQuery(self):
+ """Test modifying query element of Iq stanzas."""
+ iq = self.Iq()
+
+ iq['query'] = 'query_ns'
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="query_ns" />
+ </iq>
+ """)
+
+ iq['query'] = 'query_ns2'
+ self.checkIq(iq, """
+ <iq id="0">
+ <query xmlns="query_ns2" />
+ </iq>
+ """)
+
+ self.failUnless(iq['query'] == 'query_ns2', "Query namespace doesn't match")
+
+ del iq['query']
+ self.checkIq(iq, """
+ <iq id="0" />
+ """)
+
+ def testReply(self):
+ """Test setting proper result type in Iq replies."""
+ iq = self.Iq()
+ iq['to'] = 'user@localhost'
+ iq['type'] = 'get'
+ iq.reply()
+
+ self.checkIq(iq, """
+ <iq id="0" type="result" />
+ """)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestIqStanzas)
diff --git a/tests/test_jid.py b/tests/test_jid.py
new file mode 100644
index 00000000..cddac424
--- /dev/null
+++ b/tests/test_jid.py
@@ -0,0 +1,26 @@
+from . sleektest import *
+from sleekxmpp.xmlstream.jid import JID
+
+class TestJIDClass(SleekTest):
+ def testJIDfromfull(self):
+ j = JID('user@someserver/some/resource')
+ self.assertEqual(j.user, 'user', "User does not match")
+ self.assertEqual(j.domain, 'someserver', "Domain does not match")
+ self.assertEqual(j.resource, 'some/resource', "Resource does not match")
+ self.assertEqual(j.bare, 'user@someserver', "Bare does not match")
+ self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match")
+ self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match")
+
+ def testJIDchange(self):
+ j = JID('user1@someserver1/some1/resource1')
+ j.user = 'user'
+ j.domain = 'someserver'
+ j.resource = 'some/resource'
+ self.assertEqual(j.user, 'user', "User does not match")
+ self.assertEqual(j.domain, 'someserver', "Domain does not match")
+ self.assertEqual(j.resource, 'some/resource', "Resource does not match")
+ self.assertEqual(j.bare, 'user@someserver', "Bare does not match")
+ self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match")
+ self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match")
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestJIDClass)
diff --git a/tests/test_messagestanzas.py b/tests/test_messagestanzas.py
index 08488ce3..2a1567da 100644
--- a/tests/test_messagestanzas.py
+++ b/tests/test_messagestanzas.py
@@ -1,44 +1,57 @@
-import unittest
-from xml.etree import cElementTree as ET
-
-class testmessagestanzas(unittest.TestCase):
-
- def setUp(self):
- import sleekxmpp.stanza.message as m
- from sleekxmpp.basexmpp import stanzaPlugin
- from sleekxmpp.stanza.htmlim import HTMLIM
- stanzaPlugin(m.Message, HTMLIM)
- self.m = m
-
- def testGroupchatReplyRegression(self):
- "Regression groupchat reply should be to barejid"
- msg = self.m.Message()
- msg['to'] = 'me@myserver.tld'
- msg['from'] = 'room@someservice.someserver.tld/somenick'
- msg['type'] = 'groupchat'
- msg['body'] = "this is a message"
- msg.reply()
- self.failUnless(str(msg['to']) == 'room@someservice.someserver.tld')
-
- def testAttribProperty(self):
- "Test attrib property returning self"
- msg = self.m.Message()
- msg.attrib.attrib.attrib['to'] = 'usr@server.tld'
- self.failUnless(str(msg['to']) == 'usr@server.tld')
-
- def testHTMLPlugin(self):
- "Test message/html/html stanza"
- msgtxt = """<message to="fritzy@netflint.net/sleekxmpp" type="chat"><body>this is the plaintext message</body><html xmlns="http://jabber.org/protocol/xhtml-im"><body xmlns="http://www.w3.org/1999/xhtml"><p>This is the htmlim message</p></body></html></message>"""
- msg = self.m.Message()
- msg['to'] = "fritzy@netflint.net/sleekxmpp"
- msg['body'] = "this is the plaintext message"
- msg['type'] = 'chat'
- p = ET.Element('{http://www.w3.org/1999/xhtml}p')
- p.text = "This is the htmlim message"
- msg['html']['html'] = p
- msg2 = self.m.Message()
- values = msg.getValues()
- msg2.setValues(values)
- self.failUnless(msgtxt == str(msg) == str(msg2))
-
-suite = unittest.TestLoader().loadTestsFromTestCase(testmessagestanzas)
+from . sleektest import *
+from sleekxmpp.stanza.message import Message
+from sleekxmpp.stanza.htmlim import HTMLIM
+
+
+class TestMessageStanzas(SleekTest):
+
+ def setUp(self):
+ registerStanzaPlugin(Message, HTMLIM)
+
+ def testGroupchatReplyRegression(self):
+ "Regression groupchat reply should be to barejid"
+ msg = self.Message()
+ msg['to'] = 'me@myserver.tld'
+ msg['from'] = 'room@someservice.someserver.tld/somenick'
+ msg['type'] = 'groupchat'
+ msg['body'] = "this is a message"
+ msg.reply()
+ self.failUnless(str(msg['to']) == 'room@someservice.someserver.tld')
+
+ def testAttribProperty(self):
+ "Test attrib property returning self"
+ msg = self.Message()
+ msg.attrib.attrib.attrib['to'] = 'usr@server.tld'
+ self.failUnless(str(msg['to']) == 'usr@server.tld')
+
+ def testHTMLPlugin(self):
+ "Test message/html/body stanza"
+ msg = self.Message()
+ msg['to'] = "fritzy@netflint.net/sleekxmpp"
+ msg['body'] = "this is the plaintext message"
+ msg['type'] = 'chat'
+ p = ET.Element('{http://www.w3.org/1999/xhtml}p')
+ p.text = "This is the htmlim message"
+ msg['html']['body'] = p
+ self.checkMessage(msg, """
+ <message to="fritzy@netflint.net/sleekxmpp" type="chat">
+ <body>this is the plaintext message</body>
+ <html xmlns="http://jabber.org/protocol/xhtml-im">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p>This is the htmlim message</p>
+ </body>
+ </html>
+ </message>""")
+
+ def testNickPlugin(self):
+ "Test message/nick/nick stanza."
+ msg = self.Message()
+ msg['nick']['nick'] = 'A nickname!'
+ self.checkMessage(msg, """
+ <message>
+ <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
+ </message>
+ """)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestMessageStanzas)
diff --git a/tests/test_presencestanzas.py b/tests/test_presencestanzas.py
index 23eb911e..d6a5a388 100644
--- a/tests/test_presencestanzas.py
+++ b/tests/test_presencestanzas.py
@@ -1,31 +1,67 @@
-import unittest
-
-class testpresencestanzas(unittest.TestCase):
-
- def setUp(self):
- import sleekxmpp.stanza.presence as p
- self.p = p
-
- def testPresenceShowRegression(self):
- "Regression check presence['type'] = 'dnd' show value working"
- p = self.p.Presence()
- p['type'] = 'dnd'
- self.failUnless(str(p) == "<presence><show>dnd</show></presence>")
-
- def testPresenceUnsolicitedOffline(self):
- "Unsolicted offline presence does not spawn changed_status or update roster"
- p = self.p.Presence()
- p['type'] = 'unavailable'
- p['from'] = 'bill@chadmore.com/gmail15af'
- import sleekxmpp
- c = sleekxmpp.ClientXMPP('crap@wherever', 'password')
- happened = []
- def handlechangedpresence(event):
- happened.append(True)
- c.add_event_handler("changed_status", handlechangedpresence)
- c._handlePresence(p)
- self.failUnless(happened == [], "changed_status event triggered for superfulous unavailable presence")
- self.failUnless(c.roster == {}, "Roster updated for superfulous unavailable presence")
-
-
-suite = unittest.TestLoader().loadTestsFromTestCase(testpresencestanzas)
+import sleekxmpp
+from . sleektest import *
+from sleekxmpp.stanza.presence import Presence
+
+
+class TestPresenceStanzas(SleekTest):
+
+ def testPresenceShowRegression(self):
+ """Regression check presence['type'] = 'dnd' show value working"""
+ p = self.Presence()
+ p['type'] = 'dnd'
+ self.checkPresence(p, "<presence><show>dnd</show></presence>")
+
+ def testPresenceType(self):
+ """Test manipulating presence['type']"""
+ p = self.Presence()
+ p['type'] = 'available'
+ self.checkPresence(p, "<presence />")
+ self.failUnless(p['type'] == 'available',
+ "Incorrect presence['type'] for type 'available'")
+
+ for showtype in ['away', 'chat', 'dnd', 'xa']:
+ p['type'] = showtype
+ self.checkPresence(p, """
+ <presence><show>%s</show></presence>
+ """ % showtype)
+ self.failUnless(p['type'] == showtype,
+ "Incorrect presence['type'] for type '%s'" % showtype)
+
+ p['type'] = None
+ self.checkPresence(p, "<presence />")
+
+ def testPresenceUnsolicitedOffline(self):
+ """
+ Unsolicted offline presence does not spawn changed_status
+ or update the roster.
+ """
+ p = self.Presence()
+ p['type'] = 'unavailable'
+ p['from'] = 'bill@chadmore.com/gmail15af'
+
+ c = sleekxmpp.ClientXMPP('crap@wherever', 'password')
+ happened = []
+
+ def handlechangedpresence(event):
+ happened.append(True)
+
+ c.add_event_handler("changed_status", handlechangedpresence)
+ c._handlePresence(p)
+
+ self.failUnless(happened == [],
+ "changed_status event triggered for extra unavailable presence")
+ self.failUnless(c.roster == {},
+ "Roster updated for superfulous unavailable presence")
+
+ def testNickPlugin(self):
+ """Test presence/nick/nick stanza."""
+ p = self.Presence()
+ p['nick']['nick'] = 'A nickname!'
+ self.checkPresence(p, """
+ <presence>
+ <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
+ </presence>
+ """)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestPresenceStanzas)
diff --git a/tests/test_pubsubstanzas.py b/tests/test_pubsubstanzas.py
index 089ee180..cddfd12b 100644
--- a/tests/test_pubsubstanzas.py
+++ b/tests/test_pubsubstanzas.py
@@ -1,315 +1,511 @@
-import unittest
-from xml.etree import cElementTree as ET
-from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
-from . import xmlcompare
+from . sleektest import *
+import sleekxmpp.plugins.xep_0004 as xep_0004
+import sleekxmpp.plugins.stanza_pubsub as pubsub
-class testpubsubstanzas(unittest.TestCase):
- def setUp(self):
- import sleekxmpp.plugins.stanza_pubsub as ps
- self.ps = ps
+class TestPubsubStanzas(SleekTest):
- def testAffiliations(self):
- "Testing iq/pubsub/affiliations/affiliation stanzas"
- iq = self.ps.Iq()
- aff1 = self.ps.Affiliation()
- aff1['node'] = 'testnode'
- aff1['affiliation'] = 'owner'
- aff2 = self.ps.Affiliation()
- aff2['node'] = 'testnode2'
- aff2['affiliation'] = 'publisher'
- iq['pubsub']['affiliations'].append(aff1)
- iq['pubsub']['affiliations'].append(aff2)
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><affiliations><affiliation node="testnode" affiliation="owner" /><affiliation node="testnode2" affiliation="publisher" /></affiliations></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3), "3 methods for creating stanza don't match")
- self.failUnless(iq.match('iq@id=0/pubsub/affiliations/affiliation@node=testnode2@affiliation=publisher'), 'Match path failed')
-
- def testSubscriptions(self):
- "Testing iq/pubsub/subscriptions/subscription stanzas"
- iq = self.ps.Iq()
- sub1 = self.ps.Subscription()
- sub1['node'] = 'testnode'
- sub1['jid'] = 'steve@myserver.tld/someresource'
- sub2 = self.ps.Subscription()
- sub2['node'] = 'testnode2'
- sub2['jid'] = 'boogers@bork.top/bill'
- sub2['subscription'] = 'subscribed'
- iq['pubsub']['subscriptions'].append(sub1)
- iq['pubsub']['subscriptions'].append(sub2)
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><subscriptions><subscription node="testnode" jid="steve@myserver.tld/someresource" /><subscription node="testnode2" jid="boogers@bork.top/bill" subscription="subscribed" /></subscriptions></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testOptionalSettings(self):
- "Testing iq/pubsub/subscription/subscribe-options stanzas"
- iq = self.ps.Iq()
- iq['pubsub']['subscription']['suboptions']['required'] = True
- iq['pubsub']['subscription']['node'] = 'testnode alsdkjfas'
- iq['pubsub']['subscription']['jid'] = "fritzy@netflint.net/sleekxmpp"
- iq['pubsub']['subscription']['subscription'] = 'unconfigured'
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><subscription node="testnode alsdkjfas" jid="fritzy@netflint.net/sleekxmpp" subscription="unconfigured"><subscribe-options><required /></subscribe-options></subscription></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testItems(self):
- "Testing iq/pubsub/items stanzas"
- iq = self.ps.Iq()
- iq['pubsub']['items']
- payload = ET.fromstring("""<thinger xmlns="http://andyet.net/protocol/thinger" x="1" y='2'><child1 /><child2 normandy='cheese' foo='bar' /></thinger>""")
- payload2 = ET.fromstring("""<thinger2 xmlns="http://andyet.net/protocol/thinger2" x="12" y='22'><child12 /><child22 normandy='cheese2' foo='bar2' /></thinger2>""")
- item = self.ps.Item()
- item['id'] = 'asdf'
- item['payload'] = payload
- item2 = self.ps.Item()
- item2['id'] = 'asdf2'
- item2['payload'] = payload2
- iq['pubsub']['items'].append(item)
- iq['pubsub']['items'].append(item2)
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><items><item id="asdf"><thinger xmlns="http://andyet.net/protocol/thinger" y="2" x="1"><child1 /><child2 foo="bar" normandy="cheese" /></thinger></item><item id="asdf2"><thinger2 xmlns="http://andyet.net/protocol/thinger2" y="22" x="12"><child12 /><child22 foo="bar2" normandy="cheese2" /></thinger2></item></items></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testCreate(self):
- "Testing iq/pubsub/create&configure stanzas"
- from sleekxmpp.plugins import xep_0004
- iq = self.ps.Iq()
- iq['pubsub']['create']['node'] = 'mynode'
- form = xep_0004.Form()
- form.addField('pubsub#title', ftype='text-single', value='This thing is awesome')
- iq['pubsub']['configure']['config'] = form
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><create node="mynode" /><configure><x xmlns="jabber:x:data" type="form"><field var="pubsub#title" type="text-single"><value>This thing is awesome</value></field></x></configure></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testState(self):
- "Testing iq/psstate stanzas"
- from sleekxmpp.plugins import xep_0004
- iq = self.ps.Iq()
- iq['psstate']['node']= 'mynode'
- iq['psstate']['item']= 'myitem'
- pl = ET.Element('{http://andyet.net/protocol/pubsubqueue}claimed')
- iq['psstate']['payload'] = pl
- xmlstring = """<iq id="0"><state xmlns="http://jabber.org/protocol/psstate" node="mynode" item="myitem"><claimed xmlns="http://andyet.net/protocol/pubsubqueue" /></state></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testDefault(self):
- "Testing iq/pubsub_owner/default stanzas"
- from sleekxmpp.plugins import xep_0004
- iq = self.ps.Iq()
- iq['pubsub_owner']['default']
- iq['pubsub_owner']['default']['node'] = 'mynode'
- iq['pubsub_owner']['default']['type'] = 'leaf'
- form = xep_0004.Form()
- form.addField('pubsub#title', ftype='text-single', value='This thing is awesome')
- iq['pubsub_owner']['default']['config'] = form
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><default node="mynode" type="leaf"><x xmlns="jabber:x:data" type="form"><field var="pubsub#title" type="text-single"><value>This thing is awesome</value></field></x></default></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testSubscribe(self):
- "Testing iq/pubsub/subscribe stanzas"
- from sleekxmpp.plugins import xep_0004
- iq = self.ps.Iq()
- iq['pubsub']['subscribe']['options']
- iq['pubsub']['subscribe']['node'] = 'cheese'
- iq['pubsub']['subscribe']['jid'] = 'fritzy@netflint.net/sleekxmpp'
- iq['pubsub']['subscribe']['options']['node'] = 'cheese'
- iq['pubsub']['subscribe']['options']['jid'] = 'fritzy@netflint.net/sleekxmpp'
- form = xep_0004.Form()
- form.addField('pubsub#title', ftype='text-single', value='This thing is awesome')
- iq['pubsub']['subscribe']['options']['options'] = form
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp"><options node="cheese" jid="fritzy@netflint.net/sleekxmpp"><x xmlns="jabber:x:data" type="form"><field var="pubsub#title" type="text-single"><value>This thing is awesome</value></field></x></options></subscribe></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testPublish(self):
- "Testing iq/pubsub/publish stanzas"
- iq = self.ps.Iq()
- iq['pubsub']['publish']['node'] = 'thingers'
- payload = ET.fromstring("""<thinger xmlns="http://andyet.net/protocol/thinger" x="1" y='2'><child1 /><child2 normandy='cheese' foo='bar' /></thinger>""")
- payload2 = ET.fromstring("""<thinger2 xmlns="http://andyet.net/protocol/thinger2" x="12" y='22'><child12 /><child22 normandy='cheese2' foo='bar2' /></thinger2>""")
- item = self.ps.Item()
- item['id'] = 'asdf'
- item['payload'] = payload
- item2 = self.ps.Item()
- item2['id'] = 'asdf2'
- item2['payload'] = payload2
- iq['pubsub']['publish'].append(item)
- iq['pubsub']['publish'].append(item2)
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="thingers"><item id="asdf"><thinger xmlns="http://andyet.net/protocol/thinger" y="2" x="1"><child1 /><child2 foo="bar" normandy="cheese" /></thinger></item><item id="asdf2"><thinger2 xmlns="http://andyet.net/protocol/thinger2" y="22" x="12"><child12 /><child22 foo="bar2" normandy="cheese2" /></thinger2></item></publish></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- values = iq2.getValues()
- iq3.setValues(values)
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
+ def testAffiliations(self):
+ "Testing iq/pubsub/affiliations/affiliation stanzas"
+ iq = self.Iq()
+ aff1 = pubsub.Affiliation()
+ aff1['node'] = 'testnode'
+ aff1['affiliation'] = 'owner'
+ aff2 = pubsub.Affiliation()
+ aff2['node'] = 'testnode2'
+ aff2['affiliation'] = 'publisher'
+ iq['pubsub']['affiliations'].append(aff1)
+ iq['pubsub']['affiliations'].append(aff2)
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <affiliations>
+ <affiliation node="testnode" affiliation="owner" />
+ <affiliation node="testnode2" affiliation="publisher" />
+ </affiliations>
+ </pubsub>
+ </iq>""")
- def testDelete(self):
- "Testing iq/pubsub_owner/delete stanzas"
- iq = self.ps.Iq()
- iq['pubsub_owner']['delete']['node'] = 'thingers'
- xmlstring = """<iq id="0"><pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><delete node="thingers" /></pubsub></iq>"""
- iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring))
- iq3 = self.ps.Iq()
- iq3.setValues(iq2.getValues())
- self.failUnless(xmlstring == str(iq) == str(iq2) == str(iq3))
-
- def testCreateConfigGet(self):
- """Testing getting config from full create"""
- xml = """<iq to="pubsub.asdf" type="set" id="E" from="fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7"><pubsub xmlns="http://jabber.org/protocol/pubsub"><create node="testnode2" /><configure><x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#node_type" type="list-single" label="Select the node type"><value>leaf</value></field><field var="pubsub#title" type="text-single" label="A friendly name for the node" /><field var="pubsub#deliver_notifications" type="boolean" label="Deliver event notifications"><value>1</value></field><field var="pubsub#deliver_payloads" type="boolean" label="Deliver payloads with event notifications"><value>1</value></field><field var="pubsub#notify_config" type="boolean" label="Notify subscribers when the node configuration changes" /><field var="pubsub#notify_delete" type="boolean" label="Notify subscribers when the node is deleted" /><field var="pubsub#notify_retract" type="boolean" label="Notify subscribers when items are removed from the node"><value>1</value></field><field var="pubsub#notify_sub" type="boolean" label="Notify owners about new subscribers and unsubscribes" /><field var="pubsub#persist_items" type="boolean" label="Persist items in storage" /><field var="pubsub#max_items" type="text-single" label="Max # of items to persist"><value>10</value></field><field var="pubsub#subscribe" type="boolean" label="Whether to allow subscriptions"><value>1</value></field><field var="pubsub#access_model" type="list-single" label="Specify the subscriber model"><value>open</value></field><field var="pubsub#publish_model" type="list-single" label="Specify the publisher model"><value>publishers</value></field><field var="pubsub#send_last_published_item" type="list-single" label="Send last published item"><value>never</value></field><field var="pubsub#presence_based_delivery" type="boolean" label="Deliver notification only to available users" /></x></configure></pubsub></iq>"""
- iq = self.ps.Iq(None, self.ps.ET.fromstring(xml))
- config = iq['pubsub']['configure']['config']
- self.failUnless(config.getValues() != {})
+ def testSubscriptions(self):
+ "Testing iq/pubsub/subscriptions/subscription stanzas"
+ iq = self.Iq()
+ sub1 = pubsub.Subscription()
+ sub1['node'] = 'testnode'
+ sub1['jid'] = 'steve@myserver.tld/someresource'
+ sub2 = pubsub.Subscription()
+ sub2['node'] = 'testnode2'
+ sub2['jid'] = 'boogers@bork.top/bill'
+ sub2['subscription'] = 'subscribed'
+ iq['pubsub']['subscriptions'].append(sub1)
+ iq['pubsub']['subscriptions'].append(sub2)
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <subscriptions>
+ <subscription node="testnode" jid="steve@myserver.tld/someresource" />
+ <subscription node="testnode2" jid="boogers@bork.top/bill" subscription="subscribed" />
+ </subscriptions>
+ </pubsub>
+ </iq>""")
- def testItemEvent(self):
- """Testing message/pubsub_event/items/item"""
- msg = self.ps.Message()
- item = self.ps.EventItem()
- pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
- item['payload'] = pl
- item['id'] = 'abc123'
- msg['pubsub_event']['items'].append(item)
- msg['pubsub_event']['items']['node'] = 'cheese'
- msg['type'] = 'normal'
- xmlstring = """<message type="normal"><event xmlns="http://jabber.org/protocol/pubsub#event"><items node="cheese"><item id="abc123"><test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" /></item></items></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
+ def testOptionalSettings(self):
+ "Testing iq/pubsub/subscription/subscribe-options stanzas"
+ iq = self.Iq()
+ iq['pubsub']['subscription']['suboptions']['required'] = True
+ iq['pubsub']['subscription']['node'] = 'testnode alsdkjfas'
+ iq['pubsub']['subscription']['jid'] = "fritzy@netflint.net/sleekxmpp"
+ iq['pubsub']['subscription']['subscription'] = 'unconfigured'
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <subscription node="testnode alsdkjfas" jid="fritzy@netflint.net/sleekxmpp" subscription="unconfigured">
+ <subscribe-options>
+ <required />
+ </subscribe-options>
+ </subscription>
+ </pubsub>
+ </iq>""")
- def testItemsEvent(self):
- """Testing multiple message/pubsub_event/items/item"""
- msg = self.ps.Message()
- item = self.ps.EventItem()
- item2 = self.ps.EventItem()
- pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
- pl2 = ET.Element('{http://netflint.net/protocol/test-other}test', {'total':'27', 'failed':'3'})
- item2['payload'] = pl2
- item['payload'] = pl
- item['id'] = 'abc123'
- item2['id'] = '123abc'
- msg['pubsub_event']['items'].append(item)
- msg['pubsub_event']['items'].append(item2)
- msg['pubsub_event']['items']['node'] = 'cheese'
- msg['type'] = 'normal'
- xmlstring = """<message type="normal"><event xmlns="http://jabber.org/protocol/pubsub#event"><items node="cheese"><item id="abc123"><test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" /></item><item id="123abc"><test xmlns="http://netflint.net/protocol/test-other" failed="3" total="27" /></item></items></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
+ def testItems(self):
+ "Testing iq/pubsub/items stanzas"
+ iq = self.Iq()
+ iq['pubsub']['items']
+ payload = ET.fromstring("""
+ <thinger xmlns="http://andyet.net/protocol/thinger" x="1" y='2'>
+ <child1 />
+ <child2 normandy='cheese' foo='bar' />
+ </thinger>""")
+ payload2 = ET.fromstring("""
+ <thinger2 xmlns="http://andyet.net/protocol/thinger2" x="12" y='22'>
+ <child12 />
+ <child22 normandy='cheese2' foo='bar2' />
+ </thinger2>""")
+ item = pubsub.Item()
+ item['id'] = 'asdf'
+ item['payload'] = payload
+ item2 = pubsub.Item()
+ item2['id'] = 'asdf2'
+ item2['payload'] = payload2
+ iq['pubsub']['items'].append(item)
+ iq['pubsub']['items'].append(item2)
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <items>
+ <item id="asdf">
+ <thinger xmlns="http://andyet.net/protocol/thinger" y="2" x="1">
+ <child1 />
+ <child2 foo="bar" normandy="cheese" />
+ </thinger>
+ </item>
+ <item id="asdf2">
+ <thinger2 xmlns="http://andyet.net/protocol/thinger2" y="22" x="12">
+ <child12 />
+ <child22 foo="bar2" normandy="cheese2" />
+ </thinger2>
+ </item>
+ </items>
+ </pubsub>
+ </iq>""")
- def testItemsEvent(self):
- """Testing message/pubsub_event/items/item & retract mix"""
- msg = self.ps.Message()
- item = self.ps.EventItem()
- item2 = self.ps.EventItem()
- pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
- pl2 = ET.Element('{http://netflint.net/protocol/test-other}test', {'total':'27', 'failed':'3'})
- item2['payload'] = pl2
- retract = self.ps.EventRetract()
- retract['id'] = 'aabbcc'
- item['payload'] = pl
- item['id'] = 'abc123'
- item2['id'] = '123abc'
- msg['pubsub_event']['items'].append(item)
- msg['pubsub_event']['items'].append(retract)
- msg['pubsub_event']['items'].append(item2)
- msg['pubsub_event']['items']['node'] = 'cheese'
- msg['type'] = 'normal'
- xmlstring = """<message type="normal"><event xmlns="http://jabber.org/protocol/pubsub#event"><items node="cheese"><item id="abc123"><test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" /></item><retract id="aabbcc" /><item id="123abc"><test xmlns="http://netflint.net/protocol/test-other" failed="3" total="27" /></item></items></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
-
- def testCollectionAssociate(self):
- """Testing message/pubsub_event/collection/associate"""
- msg = self.ps.Message()
- msg['pubsub_event']['collection']['associate']['node'] = 'cheese'
- msg['pubsub_event']['collection']['node'] = 'cheeseburger'
- msg['type'] = 'headline'
- xmlstring = """<message type="headline"><event xmlns="http://jabber.org/protocol/pubsub#event"><collection node="cheeseburger"><associate node="cheese" /></collection></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
+ def testCreate(self):
+ "Testing iq/pubsub/create&configure stanzas"
+ iq = self.Iq()
+ iq['pubsub']['create']['node'] = 'mynode'
+ iq['pubsub']['configure']['form'].addField('pubsub#title',
+ ftype='text-single',
+ value='This thing is awesome')
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create node="mynode" />
+ <configure>
+ <x xmlns="jabber:x:data" type="form">
+ <field var="pubsub#title" type="text-single">
+ <value>This thing is awesome</value>
+ </field>
+ </x>
+ </configure>
+ </pubsub>
+ </iq>""")
- def testCollectionDisassociate(self):
- """Testing message/pubsub_event/collection/disassociate"""
- msg = self.ps.Message()
- msg['pubsub_event']['collection']['disassociate']['node'] = 'cheese'
- msg['pubsub_event']['collection']['node'] = 'cheeseburger'
- msg['type'] = 'headline'
- xmlstring = """<message type="headline"><event xmlns="http://jabber.org/protocol/pubsub#event"><collection node="cheeseburger"><disassociate node="cheese" /></collection></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
+ def testState(self):
+ "Testing iq/psstate stanzas"
+ iq = self.Iq()
+ iq['psstate']['node']= 'mynode'
+ iq['psstate']['item']= 'myitem'
+ pl = ET.Element('{http://andyet.net/protocol/pubsubqueue}claimed')
+ iq['psstate']['payload'] = pl
+ self.checkIq(iq, """
+ <iq id="0">
+ <state xmlns="http://jabber.org/protocol/psstate" node="mynode" item="myitem">
+ <claimed xmlns="http://andyet.net/protocol/pubsubqueue" />
+ </state>
+ </iq>""")
- def testEventConfiguration(self):
- """Testing message/pubsub_event/configuration/config"""
- msg = self.ps.Message()
- from sleekxmpp.plugins import xep_0004
- form = xep_0004.Form()
- form.addField('pubsub#title', ftype='text-single', value='This thing is awesome')
- msg['pubsub_event']['configuration']['node'] = 'cheese'
- msg['pubsub_event']['configuration']['config'] = form
- msg['type'] = 'headline'
- xmlstring = """<message type="headline"><event xmlns="http://jabber.org/protocol/pubsub#event"><configuration node="cheese"><x xmlns="jabber:x:data" type="form"><field var="pubsub#title" type="text-single"><value>This thing is awesome</value></field></x></configuration></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
-
- def testEventPurge(self):
- """Testing message/pubsub_event/purge"""
- msg = self.ps.Message()
- msg['pubsub_event']['purge']['node'] = 'pickles'
- msg['type'] = 'headline'
- xmlstring = """<message type="headline"><event xmlns="http://jabber.org/protocol/pubsub#event"><purge node="pickles" /></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlstring == str(msg) == str(msg2) == str(msg3))
-
- def testEventSubscription(self):
- """Testing message/pubsub_event/subscription"""
- msg = self.ps.Message()
- msg['pubsub_event']['subscription']['node'] = 'pickles'
- msg['pubsub_event']['subscription']['jid'] = 'fritzy@netflint.net/test'
- msg['pubsub_event']['subscription']['subid'] = 'aabb1122'
- msg['pubsub_event']['subscription']['subscription'] = 'subscribed'
- msg['pubsub_event']['subscription']['expiry'] = 'presence'
- msg['type'] = 'headline'
- xmlstring = """<message type="headline"><event xmlns="http://jabber.org/protocol/pubsub#event"><subscription node="pickles" subid="aabb1122" jid="fritzy@netflint.net/test" subscription="subscribed" expiry="presence" /></event></message>"""
- msg2 = self.ps.Message(None, self.ps.ET.fromstring(xmlstring))
- msg3 = self.ps.Message()
- msg3.setValues(msg2.getValues())
- self.failUnless(xmlcompare.comparemany([xmlstring, str(msg), str(msg2), str(msg3)]))
+ def testDefault(self):
+ "Testing iq/pubsub_owner/default stanzas"
+ iq = self.Iq()
+ iq['pubsub_owner']['default']
+ iq['pubsub_owner']['default']['node'] = 'mynode'
+ iq['pubsub_owner']['default']['type'] = 'leaf'
+ iq['pubsub_owner']['default']['form'].addField('pubsub#title',
+ ftype='text-single',
+ value='This thing is awesome')
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+ <default node="mynode" type="leaf">
+ <x xmlns="jabber:x:data" type="form">
+ <field var="pubsub#title" type="text-single">
+ <value>This thing is awesome</value>
+ </field>
+ </x>
+ </default>
+ </pubsub>
+ </iq>""", use_values=False)
-suite = unittest.TestLoader().loadTestsFromTestCase(testpubsubstanzas)
+ def testSubscribe(self):
+ "testing iq/pubsub/subscribe stanzas"
+ iq = self.Iq()
+ iq['pubsub']['subscribe']['options']
+ iq['pubsub']['subscribe']['node'] = 'cheese'
+ iq['pubsub']['subscribe']['jid'] = 'fritzy@netflint.net/sleekxmpp'
+ iq['pubsub']['subscribe']['options']['node'] = 'cheese'
+ iq['pubsub']['subscribe']['options']['jid'] = 'fritzy@netflint.net/sleekxmpp'
+ form = xep_0004.Form()
+ form.addField('pubsub#title', ftype='text-single', value='this thing is awesome')
+ iq['pubsub']['subscribe']['options']['options'] = form
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp">
+ <options node="cheese" jid="fritzy@netflint.net/sleekxmpp">
+ <x xmlns="jabber:x:data" type="form">
+ <field var="pubsub#title" type="text-single">
+ <value>this thing is awesome</value>
+ </field>
+ </x>
+ </options>
+ </subscribe>
+ </pubsub>
+ </iq>""", use_values=False)
+
+ def testPublish(self):
+ "Testing iq/pubsub/publish stanzas"
+ iq = self.Iq()
+ iq['pubsub']['publish']['node'] = 'thingers'
+ payload = ET.fromstring("""
+ <thinger xmlns="http://andyet.net/protocol/thinger" x="1" y='2'>
+ <child1 />
+ <child2 normandy='cheese' foo='bar' />
+ </thinger>""")
+ payload2 = ET.fromstring("""
+ <thinger2 xmlns="http://andyet.net/protocol/thinger2" x="12" y='22'>
+ <child12 />
+ <child22 normandy='cheese2' foo='bar2' />
+ </thinger2>""")
+ item = pubsub.Item()
+ item['id'] = 'asdf'
+ item['payload'] = payload
+ item2 = pubsub.Item()
+ item2['id'] = 'asdf2'
+ item2['payload'] = payload2
+ iq['pubsub']['publish'].append(item)
+ iq['pubsub']['publish'].append(item2)
+
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <publish node="thingers">
+ <item id="asdf">
+ <thinger xmlns="http://andyet.net/protocol/thinger" y="2" x="1">
+ <child1 />
+ <child2 foo="bar" normandy="cheese" />
+ </thinger>
+ </item>
+ <item id="asdf2">
+ <thinger2 xmlns="http://andyet.net/protocol/thinger2" y="22" x="12">
+ <child12 />
+ <child22 foo="bar2" normandy="cheese2" />
+ </thinger2>
+ </item>
+ </publish>
+ </pubsub>
+ </iq>""")
+
+ def testDelete(self):
+ "Testing iq/pubsub_owner/delete stanzas"
+ iq = self.Iq()
+ iq['pubsub_owner']['delete']['node'] = 'thingers'
+ self.checkIq(iq, """
+ <iq id="0">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
+ <delete node="thingers" />
+ </pubsub>
+ </iq>""")
+
+ def testCreateConfigGet(self):
+ """Testing getting config from full create"""
+ iq = self.Iq()
+ iq['to'] = 'pubsub.asdf'
+ iq['from'] = 'fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7'
+ iq['type'] = 'set'
+ iq['id'] = 'E'
+
+ pub = iq['pubsub']
+ pub['create']['node'] = 'testnode2'
+ pub['configure']['form']['type'] = 'submit'
+ pub['configure']['form'].setFields([
+ ('FORM_TYPE', {'type': 'hidden',
+ 'value': 'http://jabber.org/protocol/pubsub#node_config'}),
+ ('pubsub#node_type', {'type': 'list-single',
+ 'label': 'Select the node type',
+ 'value': 'leaf'}),
+ ('pubsub#title', {'type': 'text-single',
+ 'label': 'A friendly name for the node'}),
+ ('pubsub#deliver_notifications', {'type': 'boolean',
+ 'label': 'Deliver event notifications',
+ 'value': True}),
+ ('pubsub#deliver_payloads', {'type': 'boolean',
+ 'label': 'Deliver payloads with event notifications',
+ 'value': True}),
+ ('pubsub#notify_config', {'type': 'boolean',
+ 'label': 'Notify subscribers when the node configuration changes'}),
+ ('pubsub#notify_delete', {'type': 'boolean',
+ 'label': 'Notify subscribers when the node is deleted'}),
+ ('pubsub#notify_retract', {'type': 'boolean',
+ 'label': 'Notify subscribers when items are removed from the node',
+ 'value': True}),
+ ('pubsub#notify_sub', {'type': 'boolean',
+ 'label': 'Notify owners about new subscribers and unsubscribes'}),
+ ('pubsub#persist_items', {'type': 'boolean',
+ 'label': 'Persist items in storage'}),
+ ('pubsub#max_items', {'type': 'text-single',
+ 'label': 'Max # of items to persist',
+ 'value': '10'}),
+ ('pubsub#subscribe', {'type': 'boolean',
+ 'label': 'Whether to allow subscriptions',
+ 'value': True}),
+ ('pubsub#access_model', {'type': 'list-single',
+ 'label': 'Specify the subscriber model',
+ 'value': 'open'}),
+ ('pubsub#publish_model', {'type': 'list-single',
+ 'label': 'Specify the publisher model',
+ 'value': 'publishers'}),
+ ('pubsub#send_last_published_item', {'type': 'list-single',
+ 'label': 'Send last published item',
+ 'value': 'never'}),
+ ('pubsub#presence_based_delivery', {'type': 'boolean',
+ 'label': 'Deliver notification only to available users'}),
+ ])
+
+ self.checkIq(iq, """
+ <iq to="pubsub.asdf" type="set" id="E" from="fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7">
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
+ <create node="testnode2" />
+ <configure>
+ <x xmlns="jabber:x:data" type="submit">
+ <field var="FORM_TYPE" type="hidden">
+ <value>http://jabber.org/protocol/pubsub#node_config</value>
+ </field>
+ <field var="pubsub#node_type" type="list-single" label="Select the node type">
+ <value>leaf</value>
+ </field>
+ <field var="pubsub#title" type="text-single" label="A friendly name for the node" />
+ <field var="pubsub#deliver_notifications" type="boolean" label="Deliver event notifications">
+ <value>1</value>
+ </field>
+ <field var="pubsub#deliver_payloads" type="boolean" label="Deliver payloads with event notifications">
+ <value>1</value>
+ </field>
+ <field var="pubsub#notify_config" type="boolean" label="Notify subscribers when the node configuration changes" />
+ <field var="pubsub#notify_delete" type="boolean" label="Notify subscribers when the node is deleted" />
+ <field var="pubsub#notify_retract" type="boolean" label="Notify subscribers when items are removed from the node">
+ <value>1</value>
+ </field>
+ <field var="pubsub#notify_sub" type="boolean" label="Notify owners about new subscribers and unsubscribes" />
+ <field var="pubsub#persist_items" type="boolean" label="Persist items in storage" />
+ <field var="pubsub#max_items" type="text-single" label="Max # of items to persist">
+ <value>10</value>
+ </field>
+ <field var="pubsub#subscribe" type="boolean" label="Whether to allow subscriptions">
+ <value>1</value>
+ </field>
+ <field var="pubsub#access_model" type="list-single" label="Specify the subscriber model">
+ <value>open</value>
+ </field>
+ <field var="pubsub#publish_model" type="list-single" label="Specify the publisher model">
+ <value>publishers</value>
+ </field>
+ <field var="pubsub#send_last_published_item" type="list-single" label="Send last published item">
+ <value>never</value>
+ </field>
+ <field var="pubsub#presence_based_delivery" type="boolean" label="Deliver notification only to available users" />
+ </x>
+ </configure>
+ </pubsub>
+ </iq>""")
+
+ def testItemEvent(self):
+ """Testing message/pubsub_event/items/item"""
+ msg = self.Message()
+ item = pubsub.EventItem()
+ pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
+ item['payload'] = pl
+ item['id'] = 'abc123'
+ msg['pubsub_event']['items'].append(item)
+ msg['pubsub_event']['items']['node'] = 'cheese'
+ msg['type'] = 'normal'
+ self.checkMessage(msg, """
+ <message type="normal">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <items node="cheese">
+ <item id="abc123">
+ <test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" />
+ </item>
+ </items>
+ </event>
+ </message>""")
+
+ def testItemsEvent(self):
+ """Testing multiple message/pubsub_event/items/item"""
+ msg = self.Message()
+ item = pubsub.EventItem()
+ item2 = pubsub.EventItem()
+ pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
+ pl2 = ET.Element('{http://netflint.net/protocol/test-other}test', {'total':'27', 'failed':'3'})
+ item2['payload'] = pl2
+ item['payload'] = pl
+ item['id'] = 'abc123'
+ item2['id'] = '123abc'
+ msg['pubsub_event']['items'].append(item)
+ msg['pubsub_event']['items'].append(item2)
+ msg['pubsub_event']['items']['node'] = 'cheese'
+ msg['type'] = 'normal'
+ self.checkMessage(msg, """
+ <message type="normal">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <items node="cheese">
+ <item id="abc123">
+ <test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" />
+ </item>
+ <item id="123abc">
+ <test xmlns="http://netflint.net/protocol/test-other" failed="3" total="27" />
+ </item>
+ </items>
+ </event>
+ </message>""")
+
+ def testItemsEvent(self):
+ """Testing message/pubsub_event/items/item & retract mix"""
+ msg = self.Message()
+ item = pubsub.EventItem()
+ item2 = pubsub.EventItem()
+ pl = ET.Element('{http://netflint.net/protocol/test}test', {'failed':'3', 'passed':'24'})
+ pl2 = ET.Element('{http://netflint.net/protocol/test-other}test', {'total':'27', 'failed':'3'})
+ item2['payload'] = pl2
+ retract = pubsub.EventRetract()
+ retract['id'] = 'aabbcc'
+ item['payload'] = pl
+ item['id'] = 'abc123'
+ item2['id'] = '123abc'
+ msg['pubsub_event']['items'].append(item)
+ msg['pubsub_event']['items'].append(retract)
+ msg['pubsub_event']['items'].append(item2)
+ msg['pubsub_event']['items']['node'] = 'cheese'
+ msg['type'] = 'normal'
+ self.checkMessage(msg, """
+ <message type="normal">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <items node="cheese">
+ <item id="abc123">
+ <test xmlns="http://netflint.net/protocol/test" failed="3" passed="24" />
+ </item><retract id="aabbcc" />
+ <item id="123abc">
+ <test xmlns="http://netflint.net/protocol/test-other" failed="3" total="27" />
+ </item>
+ </items>
+ </event>
+ </message>""")
+
+ def testCollectionAssociate(self):
+ """Testing message/pubsub_event/collection/associate"""
+ msg = self.Message()
+ msg['pubsub_event']['collection']['associate']['node'] = 'cheese'
+ msg['pubsub_event']['collection']['node'] = 'cheeseburger'
+ msg['type'] = 'headline'
+ self.checkMessage(msg, """
+ <message type="headline">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <collection node="cheeseburger">
+ <associate node="cheese" />
+ </collection>
+ </event>
+ </message>""")
+
+ def testCollectionDisassociate(self):
+ """Testing message/pubsub_event/collection/disassociate"""
+ msg = self.Message()
+ msg['pubsub_event']['collection']['disassociate']['node'] = 'cheese'
+ msg['pubsub_event']['collection']['node'] = 'cheeseburger'
+ msg['type'] = 'headline'
+ self.checkMessage(msg, """
+ <message type="headline">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <collection node="cheeseburger">
+ <disassociate node="cheese" />
+ </collection>
+ </event>
+ </message>""")
+
+ def testEventConfiguration(self):
+ """Testing message/pubsub_event/configuration/config"""
+ msg = self.Message()
+ msg['pubsub_event']['configuration']['node'] = 'cheese'
+ msg['pubsub_event']['configuration']['form'].addField('pubsub#title',
+ ftype='text-single',
+ value='This thing is awesome')
+ msg['type'] = 'headline'
+ self.checkMessage(msg, """
+ <message type="headline">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <configuration node="cheese">
+ <x xmlns="jabber:x:data" type="form">
+ <field var="pubsub#title" type="text-single">
+ <value>This thing is awesome</value>
+ </field>
+ </x>
+ </configuration>
+ </event>
+ </message>""")
+
+ def testEventPurge(self):
+ """Testing message/pubsub_event/purge"""
+ msg = self.Message()
+ msg['pubsub_event']['purge']['node'] = 'pickles'
+ msg['type'] = 'headline'
+ self.checkMessage(msg, """
+ <message type="headline">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <purge node="pickles" />
+ </event>
+ </message>""")
+
+ def testEventSubscription(self):
+ """Testing message/pubsub_event/subscription"""
+ msg = self.Message()
+ msg['pubsub_event']['subscription']['node'] = 'pickles'
+ msg['pubsub_event']['subscription']['jid'] = 'fritzy@netflint.net/test'
+ msg['pubsub_event']['subscription']['subid'] = 'aabb1122'
+ msg['pubsub_event']['subscription']['subscription'] = 'subscribed'
+ msg['pubsub_event']['subscription']['expiry'] = 'presence'
+ msg['type'] = 'headline'
+ self.checkMessage(msg, """
+ <message type="headline">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <subscription node="pickles" subid="aabb1122" jid="fritzy@netflint.net/test" subscription="subscribed" expiry="presence" />
+ </event>
+ </message>""")
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestPubsubStanzas)
diff --git a/tests/test_roster.py b/tests/test_roster.py
new file mode 100644
index 00000000..6f9fa3d6
--- /dev/null
+++ b/tests/test_roster.py
@@ -0,0 +1,84 @@
+from . sleektest import *
+from sleekxmpp.stanza.roster import Roster
+
+
+class TestRosterStanzas(SleekTest):
+
+ def testAddItems(self):
+ """Test adding items to a roster stanza."""
+ iq = self.Iq()
+ iq['roster'].setItems({
+ 'user@example.com': {
+ 'name': 'User',
+ 'subscription': 'both',
+ 'groups': ['Friends', 'Coworkers']},
+ 'otheruser@example.com': {
+ 'name': 'Other User',
+ 'subscription': 'both',
+ 'groups': []}})
+ self.checkIq(iq, """
+ <iq>
+ <query xmlns="jabber:iq:roster">
+ <item jid="user@example.com" name="User" subscription="both">
+ <group>Friends</group>
+ <group>Coworkers</group>
+ </item>
+ <item jid="otheruser@example.com" name="Other User"
+ subscription="both" />
+ </query>
+ </iq>
+ """)
+
+ def testGetItems(self):
+ """Test retrieving items from a roster stanza."""
+ xml_string = """
+ <iq>
+ <query xmlns="jabber:iq:roster">
+ <item jid="user@example.com" name="User" subscription="both">
+ <group>Friends</group>
+ <group>Coworkers</group>
+ </item>
+ <item jid="otheruser@example.com" name="Other User"
+ subscription="both" />
+ </query>
+ </iq>
+ """
+ iq = self.Iq(ET.fromstring(xml_string))
+ expected = {
+ 'user@example.com': {
+ 'name': 'User',
+ 'subscription': 'both',
+ 'groups': ['Friends', 'Coworkers']},
+ 'otheruser@example.com': {
+ 'name': 'Other User',
+ 'subscription': 'both',
+ 'groups': []}}
+ debug = "Roster items don't match after retrieval."
+ debug += "\nReturned: %s" % str(iq['roster']['items'])
+ debug += "\nExpected: %s" % str(expected)
+ self.failUnless(iq['roster']['items'] == expected, debug)
+
+ def testDelItems(self):
+ """Test clearing items from a roster stanza."""
+ xml_string = """
+ <iq>
+ <query xmlns="jabber:iq:roster">
+ <item jid="user@example.com" name="User" subscription="both">
+ <group>Friends</group>
+ <group>Coworkers</group>
+ </item>
+ <item jid="otheruser@example.com" name="Other User"
+ subscription="both" />
+ </query>
+ </iq>
+ """
+ iq = self.Iq(ET.fromstring(xml_string))
+ del iq['roster']['items']
+ self.checkIq(iq, """
+ <iq>
+ <query xmlns="jabber:iq:roster" />
+ </iq>
+ """)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestRosterStanzas)
diff --git a/tests/test_stream.py b/tests/test_stream.py
new file mode 100644
index 00000000..6e240747
--- /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("""
+ <message to="tester@localhost" from="user@localhost">
+ <body>Hi!</body>
+ </message>
+ """)
+
+ self.streamSendMessage("""
+ <message to="user@localhost">
+ <body>Thanks for sending: Hi!</body>
+ </message>
+ """)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamTester)
diff --git a/tests/test_tostring.py b/tests/test_tostring.py
new file mode 100644
index 00000000..2999949a
--- /dev/null
+++ b/tests/test_tostring.py
@@ -0,0 +1,104 @@
+from . sleektest import *
+from sleekxmpp.stanza import Message
+from sleekxmpp.xmlstream.stanzabase import ET
+from sleekxmpp.xmlstream.tostring import tostring, xml_escape
+
+
+class TestToString(SleekTest):
+
+ """
+ Test the implementation of sleekxmpp.xmlstream.tostring
+ """
+
+ def tryTostring(self, original='', expected=None, message='', **kwargs):
+ """
+ Compare the result of calling tostring against an
+ expected result.
+ """
+ if not expected:
+ expected=original
+ if isinstance(original, str):
+ xml = ET.fromstring(original)
+ else:
+ xml=original
+ result = tostring(xml, **kwargs)
+ self.failUnless(result == expected, "%s: %s" % (message, result))
+
+ def testXMLEscape(self):
+ """Test escaping XML special characters."""
+ original = """<foo bar="baz">'Hi & welcome!'</foo>"""
+ escaped = xml_escape(original)
+ desired = """&lt;foo bar=&quot;baz&quot;&gt;&apos;Hi"""
+ desired += """ &amp; welcome!&apos;&lt;/foo&gt;"""
+
+ self.failUnless(escaped == desired,
+ "XML escaping did not work: %s." % escaped)
+
+ def testEmptyElement(self):
+ """Test converting an empty element to a string."""
+ self.tryTostring(
+ original='<bar xmlns="foo" />',
+ message="Empty element not serialized correctly")
+
+ def testEmptyElementWrapped(self):
+ """Test converting an empty element inside another element."""
+ self.tryTostring(
+ original='<bar xmlns="foo"><baz /></bar>',
+ message="Wrapped empty element not serialized correctly")
+
+ def testEmptyElementWrappedText(self):
+ """
+ Test converting an empty element wrapped with text
+ inside another element.
+ """
+ self.tryTostring(
+ original='<bar xmlns="foo">Some text. <baz /> More text.</bar>',
+ message="Text wrapped empty element serialized incorrectly")
+
+ def testMultipleChildren(self):
+ """Test converting multiple child elements to a Unicode string."""
+ self.tryTostring(
+ original='<bar xmlns="foo"><baz><qux /></baz><quux /></bar>',
+ message="Multiple child elements not serialized correctly")
+
+ def testXMLNS(self):
+ """
+ Test using xmlns tostring parameter, which will prevent adding
+ an xmlns attribute to the serialized element if the element's
+ namespace is the same.
+ """
+ self.tryTostring(
+ original='<bar xmlns="foo" />',
+ expected='<bar />',
+ message="The xmlns parameter was not used properly.",
+ xmlns='foo')
+
+ def testStanzaNs(self):
+ """
+ Test using the stanza_ns tostring parameter, which will prevent
+ adding an xmlns attribute to the serialized element if the
+ element's namespace is the same.
+ """
+ self.tryTostring(
+ original='<bar xmlns="foo" />',
+ expected='<bar />',
+ message="The stanza_ns parameter was not used properly.",
+ stanza_ns='foo')
+
+ def testStanzaStr(self):
+ """
+ Test that stanza objects are serialized properly.
+ """
+ utf8_message = '\xe0\xb2\xa0_\xe0\xb2\xa0'
+ if not hasattr(utf8_message, 'decode'):
+ # Python 3
+ utf8_message = bytes(utf8_message, encoding='utf-8')
+ msg = Message()
+ msg['body'] = utf8_message.decode('utf-8')
+ expected = '<message><body>\xe0\xb2\xa0_\xe0\xb2\xa0</body></message>'
+ result = msg.__str__()
+ self.failUnless(result == expected,
+ "Stanza Unicode handling is incorrect: %s" % result)
+
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestToString)
diff --git a/tests/xmlcompare.py b/tests/xmlcompare.py
deleted file mode 100644
index d97af971..00000000
--- a/tests/xmlcompare.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from xml.etree import cElementTree as ET
-
-def comparemany(xmls):
- xml1 = xmls[0]
- if type(xml1) == type(''):
- xml1 = ET.fromstring(xml1)
- for xml in xmls[1:]:
- xml2 = xml
- if type(xml2) == type(''):
- xml2 = ET.fromstring(xml2)
- if not compare(xml1, xml2): return False
- return True
-
-def compare(xml1, xml2):
- if xml1.tag != xml2.tag:
- return False
- if xml1.attrib != xml2.attrib:
- return False
- for child in xml1:
- child2s = xml2.findall("%s" % child.tag)
- if child2s is None:
- return False
- found = False
- for child2 in child2s:
- found = compare(child, child2)
- if found: break
- if not found: return False
- return True