From 5c76d969f79936ce56828dba2c5ab59214feec12 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sun, 27 Jun 2010 17:33:43 -0400 Subject: Added a new SleekTest class that provides useful methods for test cases. Can now use: (where self is a SleekTest instance) self.stanzaPlugin(stanza, plugin) self.Message() \ self.Iq() > Just like basexmpp.Message(), etc. self.Presence() / self.checkMessage(msg, xmlstring) self.checkIq(iq, xmlstring) self.checkPresence(pres, xmlstring) <- Not implemented yet, but stub is there. The check* methods also accept a use_values keyword argument that defaults to True. When this value is True, an additional test is executed by creating a stanza using getValues() and setValues(). Since some stanza objects can override these two methods, disabling this test is sometimes required. --- tests/sleektest.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 tests/sleektest.py (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py new file mode 100644 index 00000000..eed52ec2 --- /dev/null +++ b/tests/sleektest.py @@ -0,0 +1,159 @@ +""" + 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.txt for copying permission. +""" + +import unittest +from xml.etree import cElementTree as ET +from sleekxmpp import Message, Iq +from sleekxmpp.stanza.presence import Presence +from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath + + +class SleekTest(unittest.TestCase): + """ + A SleekXMPP specific TestCase class that provides + methods for comparing message, iq, and presence stanzas. + """ + + def stanzaPlugin(self, stanza, plugin): + """ + Associate a stanza object as a plugin for another stanza. + """ + tag = "{%s}%s" % (plugin.namespace, plugin.name) + stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin + stanza.plugin_tag_map[tag] = plugin + + def Message(self, *args, **kwargs): + """Create a message stanza.""" + return Message(None, *args, **kwargs) + + def Iq(self, *args, **kwargs): + """Create an iq stanza.""" + return Iq(None, *args, **kwargs) + + def Presence(self, *args, **kwargs): + """Create a presence stanza.""" + return Presence(None, *args, **kwargs) + + 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 getValues() and + setValues() will not be used. + """ + + debug = "Given Stanza:\n%s\n" % ET.tostring(msg.xml) + + xml = ET.fromstring(xml_string) + xml.tag = '{jabber:client}message' + debug += "XML String:\n%s\n" % ET.tostring(xml) + + msg2 = self.Message(xml) + debug += "Constructed Stanza:\n%s\n" % ET.tostring(msg2.xml) + + if use_values: + # Ugly, but need to make sure the type attribute is set. + msg['type'] = msg['type'] + if xml.attrib.get('type', None) is None: + xml.attrib['type'] = 'normal' + + values = msg2.getValues() + msg3 = self.Message() + msg3.setValues(values) + + debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml) + debug = "Three methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, msg.xml, + msg2.xml, msg3.xml]), + debug) + else: + debug = "Two methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, msg.xml, msg2.xml]), debug) + + 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 getValues() and + setValues() will not be used. + """ + debug = "Given Stanza:\n%s\n" % ET.tostring(iq.xml) + + xml = ET.fromstring(xml_string) + xml.tag = '{jabber:client}iq' + debug += "XML String:\n%s\n" % ET.tostring(xml) + + iq2 = self.Iq(xml) + debug += "Constructed Stanza:\n%s\n" % ET.tostring(iq2.xml) + + if use_values: + values = iq.getValues() + iq3 = self.Iq() + iq3.setValues(values) + + debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(iq3.xml) + debug = "Three methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, iq.xml, iq2.xml, iq3.xml]), + debug) + else: + debug = "Two methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, iq.xml, iq2.xml]), debug) + + 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 getValues() and + setValues() will not be used. + """ + pass + + def compare(self, xml1, xml2=None): + """ + Compare XML objects. + + If given a list of XML objects, then + all of the elements in the list will be + compared. + """ + + # Compare multiple objects + if type(xml1) is list: + xmls = xml1 + xml1 = xmls[0] + for xml in xmls[1:]: + xml2 = xml + if not self.compare(xml1, xml2): + return False + return True + + # Step 1: Check tags + if xml1.tag != xml2.tag: + print xml1.tag, xml2.tag + return False + + # Step 2: Check attributes + if xml1.attrib != xml2.attrib: + return False + + # Step 3: Recursively check children + for child in xml1: + child2s = xml2.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 -- cgit v1.2.3 From 35212c7991312b26e813afde3bf0bbe002058c11 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 14 Jul 2010 15:24:37 -0400 Subject: Updated SleekTest to be able to simulate and test interactions with an XML stream. --- tests/sleektest.py | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 5 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index eed52ec2..eef3b900 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -7,11 +7,80 @@ """ import unittest +import socket +try: + import queue +except ImportError: + import Queue as queue from xml.etree import cElementTree as ET +from sleekxmpp import ClientXMPP from sleekxmpp import Message, Iq from sleekxmpp.stanza.presence import Presence from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath +class TestSocket(object): + + def __init__(self, *args, **kwargs): + self.socket = socket.socket(*args, **kwargs) + self.recv_queue = queue.Queue() + self.send_queue = queue.Queue() + + def __getattr__(self, name): + """Pass requests through to actual socket""" + # Override a few methods to prevent actual socket connections + overrides = {'connect': lambda *args: None, + 'close': lambda *args: None, + 'shutdown': lambda *args: None} + return overrides.get(name, getattr(self.socket, name)) + + # ------------------------------------------------------------------ + # Testing Interface + + def nextSent(self, timeout=None): + """Get the next stanza that has been 'sent'""" + args = {'block': False} + if timeout is not None: + args = {'block': True, 'timeout': timeout} + try: + return self.send_queue.get(**args) + except: + return None + + def recvData(self, data): + """Add data to the receiving queue""" + self.recv_queue.put(data) + + # ------------------------------------------------------------------ + # Socket Interface + + def recv(self, *args, **kwargs): + return self.read(block=True) + + def send(self, data): + self.send_queue.put(data) + + # ------------------------------------------------------------------ + # File Socket + + def makefile(self, mode='r', bufsize=-1): + """File socket version to use with ElementTree""" + return self + + def read(self, size=4096, block=True, timeout=None): + """Implement the file socket interface""" + if timeout is not None: + block = True + try: + return self.recv_queue.get(block, timeout) + except: + return None + +class TestStream(object): + """Dummy class to pass a stream object to created stanzas""" + + def __init__(self): + self.default_ns = 'jabber:client' + class SleekTest(unittest.TestCase): """ @@ -27,6 +96,9 @@ class SleekTest(unittest.TestCase): stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin stanza.plugin_tag_map[tag] = plugin + # ------------------------------------------------------------------ + # Shortcut methods for creating stanza objects + def Message(self, *args, **kwargs): """Create a message stanza.""" return Message(None, *args, **kwargs) @@ -39,6 +111,9 @@ class SleekTest(unittest.TestCase): """Create a presence stanza.""" return Presence(None, *args, **kwargs) + # ------------------------------------------------------------------ + # Methods for comparing stanza objects to XML strings + def checkMessage(self, msg, xml_string, use_values=True): """ Create and compare several message stanza objects to a @@ -48,10 +123,12 @@ class SleekTest(unittest.TestCase): setValues() will not be used. """ + self.fix_namespaces(msg.xml, 'jabber:client') debug = "Given Stanza:\n%s\n" % ET.tostring(msg.xml) xml = ET.fromstring(xml_string) - xml.tag = '{jabber:client}message' + self.fix_namespaces(xml, 'jabber:client') + debug += "XML String:\n%s\n" % ET.tostring(xml) msg2 = self.Message(xml) @@ -69,8 +146,7 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, msg.xml, - msg2.xml, msg3.xml]), + self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug @@ -84,10 +160,12 @@ class SleekTest(unittest.TestCase): If use_values is False, the test using getValues() and setValues() will not be used. """ + + self.fix_namespaces(iq.xml, 'jabber:client') debug = "Given Stanza:\n%s\n" % ET.tostring(iq.xml) xml = ET.fromstring(xml_string) - xml.tag = '{jabber:client}iq' + self.fix_namespaces(xml, 'jabber:client') debug += "XML String:\n%s\n" % ET.tostring(xml) iq2 = self.Iq(xml) @@ -116,6 +194,69 @@ class SleekTest(unittest.TestCase): """ pass + # ------------------------------------------------------------------ + # Methods for simulating stanza streams. + + def streamStart(self, mode='client', skip=True): + if mode == 'client': + self.xmpp = ClientXMPP('tester@localhost', 'test') + self.xmpp.setSocket(TestSocket()) + + self.xmpp.state.set('reconnect', False) + self.xmpp.state.set('is client', True) + self.xmpp.state.set('connected', True) + + # Must have the stream header ready for xmpp.process() to work + self.xmpp.socket.recvData(self.xmpp.stream_header) + + self.xmpp.connectTCP = lambda a, b, c, d: True + self.xmpp.startTLS = lambda: True + self.xmpp.process(threaded=True) + if skip: + # Clear startup stanzas + self.xmpp.socket.nextSent(timeout=1) + + def streamRecv(self, data): + data = str(data) + self.xmpp.socket.recvData(data) + + def streamSendMessage(self, data, use_values=True, timeout=.5): + if isinstance(data, str): + data = self.Message(xml=ET.fromstring(data)) + sent = self.xmpp.socket.nextSent(timeout=1) + self.checkMessage(data, sent, use_values) + + def streamSendIq(self, data, use_values=True, timeout=.5): + if isinstance(data, str): + data = self.Iq(xml=ET.fromstring(data)) + sent = self.xmpp.socket.nextSent(timeout) + self.checkIq(data, sent, use_values) + + def streamSendPresence(self, data, use_values=True, timeout=.5): + if isinstance(data, str): + data = self.Presence(xml=ET.fromstring(data)) + sent = self.xmpp.socket.nextSent(timeout) + self.checkPresence(data, sent, use_values) + + def streamClose(self): + if self.xmpp is not None: + self.xmpp.disconnect() + self.xmpp.socket.recvData(self.xmpp.stream_footer) + + # ------------------------------------------------------------------ + # XML Comparison and Cleanup + + def fix_namespaces(self, xml, ns): + """ + Assign a namespace to an element and any children that + don't have a namespace. + """ + if xml.tag.startswith('{'): + return + xml.tag = '{%s}%s' % (ns, xml.tag) + for child in xml.getchildren(): + self.fix_namespaces(child, ns) + def compare(self, xml1, xml2=None): """ Compare XML objects. @@ -137,7 +278,6 @@ class SleekTest(unittest.TestCase): # Step 1: Check tags if xml1.tag != xml2.tag: - print xml1.tag, xml2.tag return False # Step 2: Check attributes -- cgit v1.2.3 From d5e42ac0e7282500583bf17f21eb2f944600ce76 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 19 Jul 2010 13:58:53 -0400 Subject: Condensed all of the stanzaPlugin functions into a single registerStanzaPlugin function. Updated plugins and tests to use new function. --- tests/sleektest.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index eef3b900..3c270ebd 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -17,6 +17,8 @@ from sleekxmpp import ClientXMPP from sleekxmpp import Message, Iq from sleekxmpp.stanza.presence import Presence from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin + class TestSocket(object): @@ -88,14 +90,6 @@ class SleekTest(unittest.TestCase): methods for comparing message, iq, and presence stanzas. """ - def stanzaPlugin(self, stanza, plugin): - """ - Associate a stanza object as a plugin for another stanza. - """ - tag = "{%s}%s" % (plugin.namespace, plugin.name) - stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin - stanza.plugin_tag_map[tag] = plugin - # ------------------------------------------------------------------ # Shortcut methods for creating stanza objects -- cgit v1.2.3 From f74baf1c23af79c6e732fffc7691cba97ee79715 Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Mon, 19 Jul 2010 16:25:01 -0700 Subject: updated sleektest to use new stanza get/set values api --- tests/sleektest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index 3c270ebd..d7a9d0a8 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -134,9 +134,9 @@ class SleekTest(unittest.TestCase): if xml.attrib.get('type', None) is None: xml.attrib['type'] = 'normal' - values = msg2.getValues() + values = msg2.getStanzaValues() msg3 = self.Message() - msg3.setValues(values) + msg3.setStanzaValues(values) debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml) debug = "Three methods for creating stanza do not match:\n" + debug -- cgit v1.2.3 From 85ee30539d750740cd88c0ec85247815e7eea8bb Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Mon, 19 Jul 2010 16:26:25 -0700 Subject: more set/get Values changes --- tests/sleektest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index d7a9d0a8..d5696d0b 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -166,9 +166,9 @@ class SleekTest(unittest.TestCase): debug += "Constructed Stanza:\n%s\n" % ET.tostring(iq2.xml) if use_values: - values = iq.getValues() + values = iq.getStanzaValues() iq3 = self.Iq() - iq3.setValues(values) + iq3.setStanzaValues(values) debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(iq3.xml) debug = "Three methods for creating stanza do not match:\n" + debug -- cgit v1.2.3 From 14f1c3ba512b17942e1925cc4e610d3558356ea7 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 19 Jul 2010 23:58:33 -0400 Subject: Updated SleekTest to implement the checkPresence method. Also, removed unnecessary TestStream class and shortened timeout during stream connection. --- tests/sleektest.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index d5696d0b..9f4198ec 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -77,12 +77,6 @@ class TestSocket(object): except: return None -class TestStream(object): - """Dummy class to pass a stream object to created stanzas""" - - def __init__(self): - self.default_ns = 'jabber:client' - class SleekTest(unittest.TestCase): """ @@ -186,7 +180,34 @@ class SleekTest(unittest.TestCase): If use_values is False, the test using getValues() and setValues() will not be used. """ - pass + self.fix_namespaces(pres.xml, 'jabber:client') + debug = "Given Stanza:\n%s\n" % ET.tostring(pres.xml) + + xml = ET.fromstring(xml_string) + self.fix_namespaces(xml, 'jabber:client') + debug += "XML String:\n%s\n" % ET.tostring(xml) + + pres2 = self.Presence(xml) + debug += "Constructed Stanza:\n%s\n" % ET.tostring(pres2.xml) + + # Ugly, but 'priority' has a default value and need to make + # sure it is set + pres['priority'] = pres['priority'] + pres2['priority'] = pres2['priority'] + + if use_values: + values = pres.getStanzaValues() + pres3 = self.Presence() + pres3.setStanzaValues(values) + + debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(pres3.xml) + debug = "Three methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, pres.xml, pres2.xml, pres3.xml]), + debug) + else: + debug = "Two methods for creating stanza do not match:\n" + debug + self.failUnless(self.compare([xml, pres.xml, pres2.xml]), debug) + # ------------------------------------------------------------------ # Methods for simulating stanza streams. @@ -208,7 +229,7 @@ class SleekTest(unittest.TestCase): self.xmpp.process(threaded=True) if skip: # Clear startup stanzas - self.xmpp.socket.nextSent(timeout=1) + self.xmpp.socket.nextSent(timeout=0.1) def streamRecv(self, data): data = str(data) -- cgit v1.2.3 From 690eaf8d3c3856c6242612da22e6c6d323f193ed Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 20 Jul 2010 11:19:49 -0400 Subject: Updated license notices to use the correct MIT format. Also corrected references to nonexistant license.txt to LICENSE. --- tests/sleektest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index 9f4198ec..f89966ec 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ import unittest @@ -127,6 +127,11 @@ class SleekTest(unittest.TestCase): msg['type'] = msg['type'] if xml.attrib.get('type', None) is None: xml.attrib['type'] = 'normal' + msg2['type'] = msg2['type'] + debug += ">>>>Given Stanza:\n%s\n" % ET.tostring(msg.xml) + debug += "XML String:\n%s\n" % ET.tostring(xml) + debug += ">>>>Constructed Stanza:\n%s\n" % ET.tostring(msg2.xml) + values = msg2.getStanzaValues() msg3 = self.Message() -- cgit v1.2.3 From a96a046e27289d733eabe0cb1b902e679da5d4ca Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 29 Jul 2010 23:15:49 -0400 Subject: Remove extra debugging lines and speed up stream testing in SleekTest. --- tests/sleektest.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index f89966ec..71ff105d 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -128,10 +128,7 @@ class SleekTest(unittest.TestCase): if xml.attrib.get('type', None) is None: xml.attrib['type'] = 'normal' msg2['type'] = msg2['type'] - debug += ">>>>Given Stanza:\n%s\n" % ET.tostring(msg.xml) debug += "XML String:\n%s\n" % ET.tostring(xml) - debug += ">>>>Constructed Stanza:\n%s\n" % ET.tostring(msg2.xml) - values = msg2.getStanzaValues() msg3 = self.Message() @@ -234,25 +231,25 @@ class SleekTest(unittest.TestCase): self.xmpp.process(threaded=True) if skip: # Clear startup stanzas - self.xmpp.socket.nextSent(timeout=0.1) + self.xmpp.socket.nextSent(timeout=0.01) def streamRecv(self, data): data = str(data) self.xmpp.socket.recvData(data) - def streamSendMessage(self, data, use_values=True, timeout=.5): + def streamSendMessage(self, data, use_values=True, timeout=.1): if isinstance(data, str): data = self.Message(xml=ET.fromstring(data)) - sent = self.xmpp.socket.nextSent(timeout=1) + sent = self.xmpp.socket.nextSent(timeout) self.checkMessage(data, sent, use_values) - def streamSendIq(self, data, use_values=True, timeout=.5): + def streamSendIq(self, data, use_values=True, timeout=.1): if isinstance(data, str): data = self.Iq(xml=ET.fromstring(data)) sent = self.xmpp.socket.nextSent(timeout) self.checkIq(data, sent, use_values) - def streamSendPresence(self, data, use_values=True, timeout=.5): + def streamSendPresence(self, data, use_values=True, timeout=.1): if isinstance(data, str): data = self.Presence(xml=ET.fromstring(data)) sent = self.xmpp.socket.nextSent(timeout) -- cgit v1.2.3 From 956fdf69709af8f59b92c0fe5e77490f2c670648 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 3 Aug 2010 18:31:22 -0400 Subject: Fix whitespace issues, and make some debugging statements clearer. --- tests/sleektest.py | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index 71ff105d..cea8df66 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -21,7 +21,7 @@ from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin class TestSocket(object): - + def __init__(self, *args, **kwargs): self.socket = socket.socket(*args, **kwargs) self.recv_queue = queue.Queue() @@ -104,10 +104,10 @@ class SleekTest(unittest.TestCase): def checkMessage(self, msg, xml_string, use_values=True): """ - Create and compare several message stanza objects to a - correct XML string. + Create and compare several message stanza objects to a + correct XML string. - If use_values is False, the test using getValues() and + If use_values is False, the test using getValues() and setValues() will not be used. """ @@ -118,10 +118,10 @@ class SleekTest(unittest.TestCase): self.fix_namespaces(xml, 'jabber:client') debug += "XML String:\n%s\n" % ET.tostring(xml) - + msg2 = self.Message(xml) debug += "Constructed Stanza:\n%s\n" % ET.tostring(msg2.xml) - + if use_values: # Ugly, but need to make sure the type attribute is set. msg['type'] = msg['type'] @@ -133,10 +133,10 @@ class SleekTest(unittest.TestCase): values = msg2.getStanzaValues() msg3 = self.Message() msg3.setStanzaValues(values) - + debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]), + self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug @@ -144,10 +144,10 @@ class SleekTest(unittest.TestCase): def checkIq(self, iq, xml_string, use_values=True): """ - Create and compare several iq stanza objects to a - correct XML string. + Create and compare several iq stanza objects to a + correct XML string. - If use_values is False, the test using getValues() and + If use_values is False, the test using getValues() and setValues() will not be used. """ @@ -157,10 +157,10 @@ class SleekTest(unittest.TestCase): xml = ET.fromstring(xml_string) self.fix_namespaces(xml, 'jabber:client') debug += "XML String:\n%s\n" % ET.tostring(xml) - + iq2 = self.Iq(xml) debug += "Constructed Stanza:\n%s\n" % ET.tostring(iq2.xml) - + if use_values: values = iq.getStanzaValues() iq3 = self.Iq() @@ -168,7 +168,7 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(iq3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, iq.xml, iq2.xml, iq3.xml]), + self.failUnless(self.compare([xml, iq.xml, iq2.xml, iq3.xml]), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug @@ -176,27 +176,28 @@ class SleekTest(unittest.TestCase): def checkPresence(self, pres, xml_string, use_values=True): """ - Create and compare several presence stanza objects to a - correct XML string. + Create and compare several presence stanza objects to a + correct XML string. - If use_values is False, the test using getValues() and + If use_values is False, the test using getValues() and setValues() will not be used. """ self.fix_namespaces(pres.xml, 'jabber:client') - debug = "Given Stanza:\n%s\n" % ET.tostring(pres.xml) xml = ET.fromstring(xml_string) self.fix_namespaces(xml, 'jabber:client') - debug += "XML String:\n%s\n" % ET.tostring(xml) - + pres2 = self.Presence(xml) - debug += "Constructed Stanza:\n%s\n" % ET.tostring(pres2.xml) # Ugly, but 'priority' has a default value and need to make # sure it is set pres['priority'] = pres['priority'] pres2['priority'] = pres2['priority'] + debug = "Given Stanza:\n%s\n" % ET.tostring(pres.xml) + debug += "XML String:\n%s\n" % ET.tostring(xml) + debug += "Constructed Stanza:\n%s\n" % ET.tostring(pres2.xml) + if use_values: values = pres.getStanzaValues() pres3 = self.Presence() @@ -204,7 +205,7 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(pres3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, pres.xml, pres2.xml, pres3.xml]), + self.failUnless(self.compare([xml, pres.xml, pres2.xml, pres3.xml]), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug @@ -229,7 +230,7 @@ class SleekTest(unittest.TestCase): self.xmpp.connectTCP = lambda a, b, c, d: True self.xmpp.startTLS = lambda: True self.xmpp.process(threaded=True) - if skip: + if skip: # Clear startup stanzas self.xmpp.socket.nextSent(timeout=0.01) @@ -242,7 +243,7 @@ class SleekTest(unittest.TestCase): 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): if isinstance(data, str): data = self.Iq(xml=ET.fromstring(data)) @@ -265,7 +266,7 @@ class SleekTest(unittest.TestCase): def fix_namespaces(self, xml, ns): """ - Assign a namespace to an element and any children that + Assign a namespace to an element and any children that don't have a namespace. """ if xml.tag.startswith('{'): -- cgit v1.2.3 From 48ba7292bcbc5207f08766ed3a855e16e2bf11d7 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 6 Aug 2010 12:04:52 -0400 Subject: Updated SleekTest to use the new tostring function instead of ET.tostring --- tests/sleektest.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index cea8df66..a7116544 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -18,6 +18,7 @@ from sleekxmpp import Message, Iq from sleekxmpp.stanza.presence import Presence from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.tostring import tostring class TestSocket(object): @@ -112,15 +113,15 @@ class SleekTest(unittest.TestCase): """ self.fix_namespaces(msg.xml, 'jabber:client') - debug = "Given Stanza:\n%s\n" % ET.tostring(msg.xml) + debug = "Given Stanza:\n%s\n" % tostring(msg.xml) xml = ET.fromstring(xml_string) self.fix_namespaces(xml, 'jabber:client') - debug += "XML String:\n%s\n" % ET.tostring(xml) + debug += "XML String:\n%s\n" % tostring(xml) msg2 = self.Message(xml) - debug += "Constructed Stanza:\n%s\n" % ET.tostring(msg2.xml) + debug += "Constructed Stanza:\n%s\n" % tostring(msg2.xml) if use_values: # Ugly, but need to make sure the type attribute is set. @@ -128,13 +129,13 @@ class SleekTest(unittest.TestCase): if xml.attrib.get('type', None) is None: xml.attrib['type'] = 'normal' msg2['type'] = msg2['type'] - debug += "XML String:\n%s\n" % ET.tostring(xml) + debug += "XML String:\n%s\n" % tostring(xml) values = msg2.getStanzaValues() msg3 = self.Message() msg3.setStanzaValues(values) - debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(msg3.xml) + debug += "Second Constructed Stanza:\n%s\n" % tostring(msg3.xml) debug = "Three methods for creating stanza do not match:\n" + debug self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]), debug) @@ -152,21 +153,21 @@ class SleekTest(unittest.TestCase): """ self.fix_namespaces(iq.xml, 'jabber:client') - debug = "Given Stanza:\n%s\n" % ET.tostring(iq.xml) + debug = "Given Stanza:\n%s\n" % tostring(iq.xml) xml = ET.fromstring(xml_string) self.fix_namespaces(xml, 'jabber:client') - debug += "XML String:\n%s\n" % ET.tostring(xml) + debug += "XML String:\n%s\n" % tostring(xml) iq2 = self.Iq(xml) - debug += "Constructed Stanza:\n%s\n" % ET.tostring(iq2.xml) + debug += "Constructed Stanza:\n%s\n" % tostring(iq2.xml) if use_values: values = iq.getStanzaValues() iq3 = self.Iq() iq3.setStanzaValues(values) - debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(iq3.xml) + debug += "Second Constructed Stanza:\n%s\n" % tostring(iq3.xml) debug = "Three methods for creating stanza do not match:\n" + debug self.failUnless(self.compare([xml, iq.xml, iq2.xml, iq3.xml]), debug) @@ -194,16 +195,16 @@ class SleekTest(unittest.TestCase): pres['priority'] = pres['priority'] pres2['priority'] = pres2['priority'] - debug = "Given Stanza:\n%s\n" % ET.tostring(pres.xml) - debug += "XML String:\n%s\n" % ET.tostring(xml) - debug += "Constructed Stanza:\n%s\n" % ET.tostring(pres2.xml) + debug = "Given Stanza:\n%s\n" % tostring(pres.xml) + debug += "XML String:\n%s\n" % tostring(xml) + debug += "Constructed Stanza:\n%s\n" % tostring(pres2.xml) if use_values: values = pres.getStanzaValues() pres3 = self.Presence() pres3.setStanzaValues(values) - debug += "Second Constructed Stanza:\n%s\n" % ET.tostring(pres3.xml) + debug += "Second Constructed Stanza:\n%s\n" % tostring(pres3.xml) debug = "Three methods for creating stanza do not match:\n" + debug self.failUnless(self.compare([xml, pres.xml, pres2.xml, pres3.xml]), debug) -- cgit v1.2.3 From 7a5ef2849218e122b04e244aeedd67844a0690b2 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 11 Aug 2010 18:41:57 -0400 Subject: Updated SleekTest.streamClose to check that the stream was actually started before closing it. Updated tests for Iq stanzas to not start a stream for every test; tests now run a lot faster. The call to streamClose must still be in the tearDown method to ensure it is called in the case of an error. --- tests/sleektest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index a7116544..0adab2b2 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -258,7 +258,7 @@ class SleekTest(unittest.TestCase): self.checkPresence(data, sent, use_values) def streamClose(self): - if self.xmpp is not None: + if hasattr(self, 'xmpp') and self.xmpp is not None: self.xmpp.disconnect() self.xmpp.socket.recvData(self.xmpp.stream_footer) -- cgit v1.2.3 From 22134c302b68f37ded406ea335aca4c9bdab3090 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 12 Aug 2010 01:25:42 -0400 Subject: Updated SleekTest with docs and PEP8 style. --- tests/sleektest.py | 293 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 245 insertions(+), 48 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index 0adab2b2..c7c72410 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -9,38 +9,73 @@ import unittest import socket try: - import queue + import queue except ImportError: - import Queue as queue -from xml.etree import cElementTree as ET + import Queue as queue + from sleekxmpp import ClientXMPP -from sleekxmpp import Message, Iq -from sleekxmpp.stanza.presence import Presence -from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath -from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +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): - """Pass requests through to actual socket""" - # Override a few methods to prevent actual socket connections - overrides = {'connect': lambda *args: None, - 'close': lambda *args: None, - 'shutdown': lambda *args: None} + """ + Return 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'""" + """ + 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} @@ -50,27 +85,58 @@ class TestSocket(object): return None def recvData(self, data): - """Add data to the receiving queue""" + """ + 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, mode='r', bufsize=-1): - """File socket version to use with ElementTree""" + def makefile(self, *args, **kwargs): + """ + File socket version to use with ElementTree. + + Arguments: + Placeholders, same as socket.Socket.makefile() + """ return self - def read(self, size=4096, block=True, timeout=None): - """Implement the file socket interface""" + 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: @@ -80,24 +146,65 @@ class TestSocket(object): 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.""" + """ + 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.""" + """ + 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.""" + """ + 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) # ------------------------------------------------------------------ @@ -108,8 +215,15 @@ class SleekTest(unittest.TestCase): Create and compare several message stanza objects to a correct XML string. - If use_values is False, the test using getValues() and - setValues() will not be used. + 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. """ self.fix_namespaces(msg.xml, 'jabber:client') @@ -137,19 +251,26 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % tostring(msg3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, msg.xml, msg2.xml, msg3.xml]), + self.failUnless(self.compare(xml, msg.xml, msg2.xml, msg3.xml), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, msg.xml, msg2.xml]), debug) + self.failUnless(self.compare(xml, msg.xml, msg2.xml), debug) 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 getValues() and - setValues() will not be used. + 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. """ self.fix_namespaces(iq.xml, 'jabber:client') @@ -169,20 +290,28 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % tostring(iq3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, iq.xml, iq2.xml, iq3.xml]), + self.failUnless(self.compare(xml, iq.xml, iq2.xml, iq3.xml), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, iq.xml, iq2.xml]), debug) + self.failUnless(self.compare(xml, iq.xml, iq2.xml), debug) 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 getValues() and - setValues() will not be used. + 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. """ + self.fix_namespaces(pres.xml, 'jabber:client') xml = ET.fromstring(xml_string) @@ -206,17 +335,26 @@ class SleekTest(unittest.TestCase): debug += "Second Constructed Stanza:\n%s\n" % tostring(pres3.xml) debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, pres.xml, pres2.xml, pres3.xml]), + self.failUnless(self.compare(xml, pres.xml, pres2.xml, pres3.xml), debug) else: debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare([xml, pres.xml, pres2.xml]), debug) - + self.failUnless(self.compare(xml, pres.xml, pres2.xml), debug) # ------------------------------------------------------------------ # 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()) @@ -236,28 +374,82 @@ class SleekTest(unittest.TestCase): 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) @@ -269,6 +461,10 @@ class SleekTest(unittest.TestCase): """ 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 @@ -276,36 +472,37 @@ class SleekTest(unittest.TestCase): for child in xml.getchildren(): self.fix_namespaces(child, ns) - def compare(self, xml1, xml2=None): + def compare(self, xml, *other): """ Compare XML objects. - If given a list of XML objects, then - all of the elements in the list will be - compared. + 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 type(xml1) is list: - xmls = xml1 - xml1 = xmls[0] - for xml in xmls[1:]: - xml2 = xml - if not self.compare(xml1, xml2): + 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 xml1.tag != xml2.tag: + if xml.tag != other.tag: return False # Step 2: Check attributes - if xml1.attrib != xml2.attrib: + if xml.attrib != other.attrib: return False # Step 3: Recursively check children - for child in xml1: - child2s = xml2.findall("%s" % child.tag) + for child in xml: + child2s = other.findall("%s" % child.tag) if child2s is None: return False for child2 in child2s: -- cgit v1.2.3 From c721fb412618662d33fa73fb9f9e1f0c4f045fef Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 13 Aug 2010 12:23:34 -0400 Subject: Added a generic checkStanza method to SleekTest. Updated the other check methods to use it. --- tests/sleektest.py | 168 +++++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 82 deletions(-) (limited to 'tests/sleektest.py') diff --git a/tests/sleektest.py b/tests/sleektest.py index c7c72410..801253d3 100644 --- a/tests/sleektest.py +++ b/tests/sleektest.py @@ -13,6 +13,7 @@ try: 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 @@ -210,6 +211,84 @@ class SleekTest(unittest.TestCase): # ------------------------------------------------------------------ # 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 @@ -226,36 +305,9 @@ class SleekTest(unittest.TestCase): to True. """ - self.fix_namespaces(msg.xml, 'jabber:client') - debug = "Given Stanza:\n%s\n" % tostring(msg.xml) - - xml = ET.fromstring(xml_string) - self.fix_namespaces(xml, 'jabber:client') - - debug += "XML String:\n%s\n" % tostring(xml) - - msg2 = self.Message(xml) - debug += "Constructed Stanza:\n%s\n" % tostring(msg2.xml) - - if use_values: - # Ugly, but need to make sure the type attribute is set. - msg['type'] = msg['type'] - if xml.attrib.get('type', None) is None: - xml.attrib['type'] = 'normal' - msg2['type'] = msg2['type'] - debug += "XML String:\n%s\n" % tostring(xml) - - values = msg2.getStanzaValues() - msg3 = self.Message() - msg3.setStanzaValues(values) - - debug += "Second Constructed Stanza:\n%s\n" % tostring(msg3.xml) - debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, msg.xml, msg2.xml, msg3.xml), - debug) - else: - debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, msg.xml, msg2.xml), debug) + return self.checkStanza(Message, msg, xml_string, + defaults=['type'], + use_values = use_values) def checkIq(self, iq, xml_string, use_values=True): """ @@ -272,29 +324,7 @@ class SleekTest(unittest.TestCase): and setStanzaValues should be used. Defaults to True. """ - - self.fix_namespaces(iq.xml, 'jabber:client') - debug = "Given Stanza:\n%s\n" % tostring(iq.xml) - - xml = ET.fromstring(xml_string) - self.fix_namespaces(xml, 'jabber:client') - debug += "XML String:\n%s\n" % tostring(xml) - - iq2 = self.Iq(xml) - debug += "Constructed Stanza:\n%s\n" % tostring(iq2.xml) - - if use_values: - values = iq.getStanzaValues() - iq3 = self.Iq() - iq3.setStanzaValues(values) - - debug += "Second Constructed Stanza:\n%s\n" % tostring(iq3.xml) - debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, iq.xml, iq2.xml, iq3.xml), - debug) - else: - debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, iq.xml, iq2.xml), debug) + return self.checkStanza(Iq, iq, xml_string, use_values=use_values) def checkPresence(self, pres, xml_string, use_values=True): """ @@ -311,35 +341,9 @@ class SleekTest(unittest.TestCase): and setStanzaValues should be used. Defaults to True. """ - - self.fix_namespaces(pres.xml, 'jabber:client') - - xml = ET.fromstring(xml_string) - self.fix_namespaces(xml, 'jabber:client') - - pres2 = self.Presence(xml) - - # Ugly, but 'priority' has a default value and need to make - # sure it is set - pres['priority'] = pres['priority'] - pres2['priority'] = pres2['priority'] - - debug = "Given Stanza:\n%s\n" % tostring(pres.xml) - debug += "XML String:\n%s\n" % tostring(xml) - debug += "Constructed Stanza:\n%s\n" % tostring(pres2.xml) - - if use_values: - values = pres.getStanzaValues() - pres3 = self.Presence() - pres3.setStanzaValues(values) - - debug += "Second Constructed Stanza:\n%s\n" % tostring(pres3.xml) - debug = "Three methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, pres.xml, pres2.xml, pres3.xml), - debug) - else: - debug = "Two methods for creating stanza do not match:\n" + debug - self.failUnless(self.compare(xml, pres.xml, pres2.xml), debug) + return self.checkStanza(Presence, pres, xml_string, + defaults=['priority'], + use_values=use_values) # ------------------------------------------------------------------ # Methods for simulating stanza streams. -- cgit v1.2.3