Compare commits
	
		
			15 Commits
		
	
	
		
			1.0-Beta1
			...
			sleek-1.0-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5bdcd9ef9d | ||
|   | 2eff35cc7a | ||
|   | ac330b5c6c | ||
|   | 46ffa8e9fe | ||
|   | 03847497cc | ||
|   | 185d7cf28e | ||
|   | 8aa3d0c047 | ||
|   | 9e3d506651 | ||
|   | 2f3ff37a24 | ||
|   | 1f09d60a52 | ||
|   | d528884723 | ||
|   | d9aff3d36f | ||
|   | 04cc48775d | ||
|   | 27ebb6e8f6 | ||
|   | 8f55704928 | 
| @@ -553,6 +553,7 @@ class BaseXMPP(XMLStream): | |||||||
|         priority = presence['priority'] |         priority = presence['priority'] | ||||||
|  |  | ||||||
|         was_offline = False |         was_offline = False | ||||||
|  |         got_online = False | ||||||
|         old_roster = self.roster.get(jid, {}).get(resource, {}) |         old_roster = self.roster.get(jid, {}).get(resource, {}) | ||||||
|  |  | ||||||
|         # Create a new roster entry if needed. |         # Create a new roster entry if needed. | ||||||
| @@ -569,7 +570,7 @@ class BaseXMPP(XMLStream): | |||||||
|         # Determine if the user has just come online. |         # Determine if the user has just come online. | ||||||
|         if not resource in connections: |         if not resource in connections: | ||||||
|             if show == 'available' or show in presence.showtypes: |             if show == 'available' or show in presence.showtypes: | ||||||
|                 self.event("got_online", presence) |                 got_online = True | ||||||
|             was_offline = True |             was_offline = True | ||||||
|             connections[resource] = {} |             connections[resource] = {} | ||||||
|  |  | ||||||
| @@ -601,6 +602,8 @@ class BaseXMPP(XMLStream): | |||||||
|  |  | ||||||
|         # Presence state has changed. |         # Presence state has changed. | ||||||
|         self.event("changed_status", presence) |         self.event("changed_status", presence) | ||||||
|  |         if got_online: | ||||||
|  |             self.event("got_online", presence) | ||||||
|         logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, |         logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, | ||||||
|                                                    show, status)) |                                                    show, status)) | ||||||
|  |  | ||||||
| @@ -618,8 +621,8 @@ class BaseXMPP(XMLStream): | |||||||
|         None       *          Disable automatic handling and use |         None       *          Disable automatic handling and use | ||||||
|                               a custom handler. |                               a custom handler. | ||||||
|         """ |         """ | ||||||
|         presence = self.Presence() |         presence.reply() | ||||||
|         presence['to'] = presence['from'].bare |         presence['to'] = presence['to'].bare | ||||||
|  |  | ||||||
|         # We are using trinary logic, so conditions have to be |         # We are using trinary logic, so conditions have to be | ||||||
|         # more explicit than usual. |         # more explicit than usual. | ||||||
|   | |||||||
| @@ -219,8 +219,8 @@ class ClientXMPP(BaseXMPP): | |||||||
|         iq['roster']['items'] = {jid: {'name': name, |         iq['roster']['items'] = {jid: {'name': name, | ||||||
|                                        'subscription': subscription, |                                        'subscription': subscription, | ||||||
|                                        'groups': groups}} |                                        'groups': groups}} | ||||||
|         resp = iq.send() |         response = iq.send() | ||||||
|         return resp['type'] == 'result' |         return response['type'] == 'result' | ||||||
|  |  | ||||||
|     def del_roster_item(self, jid): |     def del_roster_item(self, jid): | ||||||
|         """ |         """ | ||||||
| @@ -235,8 +235,8 @@ class ClientXMPP(BaseXMPP): | |||||||
|     def get_roster(self): |     def get_roster(self): | ||||||
|         """Request the roster from the server.""" |         """Request the roster from the server.""" | ||||||
|         iq = self.Iq()._set_stanza_values({'type': 'get'}).enable('roster') |         iq = self.Iq()._set_stanza_values({'type': 'get'}).enable('roster') | ||||||
|         iq.send() |         response = iq.send() | ||||||
|         self._handle_roster(iq, request=True) |         self._handle_roster(response, request=True) | ||||||
|  |  | ||||||
|     def _handle_stream_features(self, features): |     def _handle_stream_features(self, features): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -67,6 +67,8 @@ class ComponentXMPP(BaseXMPP): | |||||||
|         self.server_port = port |         self.server_port = port | ||||||
|         self.set_jid(jid) |         self.set_jid(jid) | ||||||
|         self.secret = secret |         self.secret = secret | ||||||
|  |         self.plugin_config = plugin_config | ||||||
|  |         self.plugin_whitelist = plugin_whitelist | ||||||
|         self.is_component = True |         self.is_component = True | ||||||
|  |  | ||||||
|         self.register_handler( |         self.register_handler( | ||||||
|   | |||||||
| @@ -38,6 +38,9 @@ class XMPPError(Exception): | |||||||
|                               element. Same as the additional arguments to |                               element. Same as the additional arguments to | ||||||
|                               the ET.Element constructor. |                               the ET.Element constructor. | ||||||
|         """ |         """ | ||||||
|  |         if extension_args is None: | ||||||
|  |             extension_args = {} | ||||||
|  |  | ||||||
|         self.condition = condition |         self.condition = condition | ||||||
|         self.text = text |         self.text = text | ||||||
|         self.etype = etype |         self.etype = etype | ||||||
|   | |||||||
| @@ -92,6 +92,12 @@ class Presence(RootStanza): | |||||||
|  |  | ||||||
|         return StanzaBase.setup(self, xml) |         return StanzaBase.setup(self, xml) | ||||||
|  |  | ||||||
|  |     def exception(self, e): | ||||||
|  |         """ | ||||||
|  |         Override exception passback for presence. | ||||||
|  |         """ | ||||||
|  |         pass | ||||||
|  |  | ||||||
|     def set_show(self, show): |     def set_show(self, show): | ||||||
|         """ |         """ | ||||||
|         Set the value of the <show> element. |         Set the value of the <show> element. | ||||||
|   | |||||||
| @@ -103,6 +103,41 @@ class SleekTest(unittest.TestCase): | |||||||
|         """ |         """ | ||||||
|         return Presence(None, *args, **kwargs) |         return Presence(None, *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def check_JID(self, jid, user=None, domain=None, resource=None, | ||||||
|  |                  bare=None, full=None, string=None): | ||||||
|  |         """ | ||||||
|  |         Verify the components of a JID. | ||||||
|  |  | ||||||
|  |         Arguments: | ||||||
|  |             jid      -- The JID object to test. | ||||||
|  |             user     -- Optional. The user name portion of the JID. | ||||||
|  |             domain   -- Optional. The domain name portion of the JID. | ||||||
|  |             resource -- Optional. The resource portion of the JID. | ||||||
|  |             bare     -- Optional. The bare JID. | ||||||
|  |             full     -- Optional. The full JID. | ||||||
|  |             string   -- Optional. The string version of the JID. | ||||||
|  |         """ | ||||||
|  |         if user is not None: | ||||||
|  |             self.assertEqual(jid.user, user, | ||||||
|  |                     "User does not match: %s" % jid.user) | ||||||
|  |         if domain is not None: | ||||||
|  |             self.assertEqual(jid.domain, domain, | ||||||
|  |                     "Domain does not match: %s" % jid.domain) | ||||||
|  |         if resource is not None: | ||||||
|  |             self.assertEqual(jid.resource, resource, | ||||||
|  |                     "Resource does not match: %s" % jid.resource) | ||||||
|  |         if bare is not None: | ||||||
|  |             self.assertEqual(jid.bare, bare, | ||||||
|  |                     "Bare JID does not match: %s" % jid.bare) | ||||||
|  |         if full is not None: | ||||||
|  |             self.assertEqual(jid.full, full, | ||||||
|  |                     "Full JID does not match: %s" % jid.full) | ||||||
|  |         if string is not None: | ||||||
|  |             self.assertEqual(str(jid), string, | ||||||
|  |                     "String does not match: %s" % str(jid)) | ||||||
|  |  | ||||||
|     # ------------------------------------------------------------------ |     # ------------------------------------------------------------------ | ||||||
|     # Methods for comparing stanza objects to XML strings |     # Methods for comparing stanza objects to XML strings | ||||||
|  |  | ||||||
| @@ -294,6 +329,7 @@ class SleekTest(unittest.TestCase): | |||||||
|         else: |         else: | ||||||
|             raise ValueError("Unknown socket type.") |             raise ValueError("Unknown socket type.") | ||||||
|  |  | ||||||
|  |         self.xmpp.register_plugins() | ||||||
|         self.xmpp.process(threaded=True) |         self.xmpp.process(threaded=True) | ||||||
|         if skip: |         if skip: | ||||||
|             # Clear startup stanzas |             # Clear startup stanzas | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ class JID(object): | |||||||
|                     full, or bare. |                     full, or bare. | ||||||
|         """ |         """ | ||||||
|         if name == 'resource': |         if name == 'resource': | ||||||
|             if self._resource is None: |             if self._resource is None and '/' in self._jid: | ||||||
|                 self._resource = self._jid.split('/', 1)[-1] |                 self._resource = self._jid.split('/', 1)[-1] | ||||||
|             return self._resource or "" |             return self._resource or "" | ||||||
|         elif name == 'user': |         elif name == 'user': | ||||||
| @@ -94,21 +94,15 @@ class JID(object): | |||||||
|         elif name in ('server', 'domain', 'host'): |         elif name in ('server', 'domain', 'host'): | ||||||
|             self.domain = value |             self.domain = value | ||||||
|         elif name in ('full', 'jid'): |         elif name in ('full', 'jid'): | ||||||
|             if '@' not in value: |             self.reset(value) | ||||||
|                 if '/' in value: |             self.regenerate() | ||||||
|                     d, r = value.split('/', 1) |  | ||||||
|                     object.__setattr__(self, "_resource", r) |  | ||||||
|                 else: |  | ||||||
|                     d = value |  | ||||||
|                 object.__setattr__(self, "_domain", d) |  | ||||||
|             else: |  | ||||||
|                 self.reset(value) |  | ||||||
|         elif name == 'bare': |         elif name == 'bare': | ||||||
|             if '@' in value: |             if '@' in value: | ||||||
|                 u, d = value.split('@', 1) |                 u, d = value.split('@', 1) | ||||||
|                 object.__setattr__(self, "_user", u) |                 object.__setattr__(self, "_user", u) | ||||||
|                 object.__setattr__(self, "_domain", d) |                 object.__setattr__(self, "_domain", d) | ||||||
|             else: |             else: | ||||||
|  |                 object.__setattr__(self, "_user", '') | ||||||
|                 object.__setattr__(self, "_domain", value) |                 object.__setattr__(self, "_domain", value) | ||||||
|             self.regenerate() |             self.regenerate() | ||||||
|         else: |         else: | ||||||
|   | |||||||
| @@ -68,9 +68,6 @@ def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''): | |||||||
|             for child in xml.getchildren(): |             for child in xml.getchildren(): | ||||||
|                 output.append(tostring(child, tag_xmlns, stanza_ns, stream)) |                 output.append(tostring(child, tag_xmlns, stanza_ns, stream)) | ||||||
|         output.append(u"</%s>" % tag_name) |         output.append(u"</%s>" % tag_name) | ||||||
|         if xml.tail: |  | ||||||
|             # If there is additional text after the element. |  | ||||||
|             output.append(xml_escape(xml.tail)) |  | ||||||
|     elif xml.text: |     elif xml.text: | ||||||
|         # If we only have text content. |         # If we only have text content. | ||||||
|         output.append(u">%s</%s>" % (xml_escape(xml.text), tag_name)) |         output.append(u">%s</%s>" % (xml_escape(xml.text), tag_name)) | ||||||
|   | |||||||
| @@ -786,6 +786,23 @@ class XMLStream(object): | |||||||
|         if unhandled: |         if unhandled: | ||||||
|             stanza.unhandled() |             stanza.unhandled() | ||||||
|  |  | ||||||
|  |     def _threaded_event_wrapper(self, func, args): | ||||||
|  |         """ | ||||||
|  |         Capture exceptions for event handlers that run  | ||||||
|  |         in individual threads. | ||||||
|  |  | ||||||
|  |         Arguments: | ||||||
|  |             func -- The event handler to execute. | ||||||
|  |             args -- Arguments to the event handler. | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             func(*args) | ||||||
|  |         except Exception as e: | ||||||
|  |             error_msg = 'Error processing event handler: %s' | ||||||
|  |             logging.exception(error_msg % str(func)) | ||||||
|  |             if hasattr(args[0], 'exception'): | ||||||
|  |                 args[0].exception(e) | ||||||
|  |  | ||||||
|     def _event_runner(self): |     def _event_runner(self): | ||||||
|         """ |         """ | ||||||
|         Process the event queue and execute handlers. |         Process the event queue and execute handlers. | ||||||
| @@ -825,14 +842,18 @@ class XMLStream(object): | |||||||
|                     func, threaded, disposable = handler |                     func, threaded, disposable = handler | ||||||
|                     try: |                     try: | ||||||
|                         if threaded: |                         if threaded: | ||||||
|                             x = threading.Thread(name="Event_%s" % str(func), |                             x = threading.Thread( | ||||||
|                                                  target=func, |                                     name="Event_%s" % str(func), | ||||||
|                                                  args=args) |                                     target=self._threaded_event_wrapper, | ||||||
|  |                                     args=(func, args)) | ||||||
|                             x.start() |                             x.start() | ||||||
|                         else: |                         else: | ||||||
|                             func(*args) |                             func(*args) | ||||||
|                     except: |                     except Exception as e: | ||||||
|                         logging.exception('Error processing event handler: %s') |                         error_msg = 'Error processing event handler: %s' | ||||||
|  |                         logging.exception(error_msg % str(func)) | ||||||
|  |                         if hasattr(args[0], 'exception'): | ||||||
|  |                             args[0].exception(e) | ||||||
|                 elif etype == 'quit': |                 elif etype == 'quit': | ||||||
|                     logging.debug("Quitting event runner thread") |                     logging.debug("Quitting event runner thread") | ||||||
|                     return False |                     return False | ||||||
|   | |||||||
| @@ -3,26 +3,126 @@ from sleekxmpp.xmlstream.jid import JID | |||||||
|  |  | ||||||
|  |  | ||||||
| class TestJIDClass(SleekTest): | class TestJIDClass(SleekTest): | ||||||
|     def testJIDfromfull(self): |  | ||||||
|         j = JID('user@someserver/some/resource') |     """Verify that the JID class can parse and manipulate JIDs.""" | ||||||
|         self.assertEqual(j.user, 'user', "User does not match") |  | ||||||
|         self.assertEqual(j.domain, 'someserver', "Domain does not match") |     def testJIDFromFull(self): | ||||||
|         self.assertEqual(j.resource, 'some/resource', "Resource does not match") |         """Test using JID of the form 'user@server/resource/with/slashes'.""" | ||||||
|         self.assertEqual(j.bare, 'user@someserver', "Bare does not match") |         self.check_JID(JID('user@someserver/some/resource'), | ||||||
|         self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match") |                        'user', | ||||||
|         self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match") |                        'someserver', | ||||||
|  |                        'some/resource', | ||||||
|  |                        'user@someserver', | ||||||
|  |                        'user@someserver/some/resource', | ||||||
|  |                        'user@someserver/some/resource') | ||||||
|  |  | ||||||
|     def testJIDchange(self): |     def testJIDchange(self): | ||||||
|  |         """Test changing JID of the form 'user@server/resource/with/slashes'""" | ||||||
|         j = JID('user1@someserver1/some1/resource1') |         j = JID('user1@someserver1/some1/resource1') | ||||||
|         j.user = 'user' |         j.user = 'user' | ||||||
|         j.domain = 'someserver' |         j.domain = 'someserver' | ||||||
|         j.resource = 'some/resource' |         j.resource = 'some/resource' | ||||||
|         self.assertEqual(j.user, 'user', "User does not match") |         self.check_JID(j, | ||||||
|         self.assertEqual(j.domain, 'someserver', "Domain does not match") |                        'user', | ||||||
|         self.assertEqual(j.resource, 'some/resource', "Resource does not match") |                        'someserver', | ||||||
|         self.assertEqual(j.bare, 'user@someserver', "Bare does not match") |                        'some/resource', | ||||||
|         self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match") |                        'user@someserver', | ||||||
|         self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match") |                        'user@someserver/some/resource', | ||||||
|  |                        'user@someserver/some/resource') | ||||||
|  |  | ||||||
|  |     def testJIDaliases(self): | ||||||
|  |         """Test changing JID using aliases for domain.""" | ||||||
|  |         j = JID('user@someserver/resource') | ||||||
|  |         j.server = 'anotherserver' | ||||||
|  |         self.check_JID(j, domain='anotherserver') | ||||||
|  |         j.host = 'yetanother' | ||||||
|  |         self.check_JID(j, domain='yetanother') | ||||||
|  |  | ||||||
|  |     def testJIDSetFullWithUser(self): | ||||||
|  |         """Test setting the full JID with a user portion.""" | ||||||
|  |         j = JID('user@domain/resource') | ||||||
|  |         j.full = 'otheruser@otherdomain/otherresource' | ||||||
|  |         self.check_JID(j, | ||||||
|  |                        'otheruser', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherresource', | ||||||
|  |                        'otheruser@otherdomain', | ||||||
|  |                        'otheruser@otherdomain/otherresource', | ||||||
|  |                        'otheruser@otherdomain/otherresource') | ||||||
|  |  | ||||||
|  |     def testJIDFullNoUserWithResource(self): | ||||||
|  |         """ | ||||||
|  |         Test setting the full JID without a user | ||||||
|  |         portion and with a resource. | ||||||
|  |         """ | ||||||
|  |         j = JID('user@domain/resource') | ||||||
|  |         j.full = 'otherdomain/otherresource' | ||||||
|  |         self.check_JID(j, | ||||||
|  |                        '', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherresource', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherdomain/otherresource', | ||||||
|  |                        'otherdomain/otherresource') | ||||||
|  |  | ||||||
|  |     def testJIDFullNoUserNoResource(self): | ||||||
|  |         """ | ||||||
|  |         Test setting the full JID without a user | ||||||
|  |         portion and without a resource. | ||||||
|  |         """ | ||||||
|  |         j = JID('user@domain/resource') | ||||||
|  |         j.full = 'otherdomain' | ||||||
|  |         self.check_JID(j, | ||||||
|  |                        '', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        '', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherdomain') | ||||||
|  |  | ||||||
|  |     def testJIDBareUser(self): | ||||||
|  |         """Test setting the bare JID with a user.""" | ||||||
|  |         j = JID('user@domain/resource') | ||||||
|  |         j.bare = 'otheruser@otherdomain' | ||||||
|  |         self.check_JID(j, | ||||||
|  |                        'otheruser', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'resource', | ||||||
|  |                        'otheruser@otherdomain', | ||||||
|  |                        'otheruser@otherdomain/resource', | ||||||
|  |                        'otheruser@otherdomain/resource') | ||||||
|  |  | ||||||
|  |     def testJIDBareNoUser(self): | ||||||
|  |         """Test setting the bare JID without a user.""" | ||||||
|  |         j = JID('user@domain/resource') | ||||||
|  |         j.bare = 'otherdomain' | ||||||
|  |         self.check_JID(j, | ||||||
|  |                        '', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'resource', | ||||||
|  |                        'otherdomain', | ||||||
|  |                        'otherdomain/resource', | ||||||
|  |                        'otherdomain/resource') | ||||||
|  |  | ||||||
|  |     def testJIDNoResource(self): | ||||||
|  |         """Test using JID of the form 'user@domain'.""" | ||||||
|  |         self.check_JID(JID('user@someserver'), | ||||||
|  |                        'user', | ||||||
|  |                        'someserver', | ||||||
|  |                        '', | ||||||
|  |                        'user@someserver', | ||||||
|  |                        'user@someserver', | ||||||
|  |                        'user@someserver') | ||||||
|  |  | ||||||
|  |     def testJIDNoUser(self): | ||||||
|  |         """Test JID of the form 'component.domain.tld'.""" | ||||||
|  |         self.check_JID(JID('component.someserver'), | ||||||
|  |                        '', | ||||||
|  |                        'component.someserver', | ||||||
|  |                        '', | ||||||
|  |                        'component.someserver', | ||||||
|  |                        'component.someserver', | ||||||
|  |                        'component.someserver') | ||||||
|  |  | ||||||
|  |  | ||||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestJIDClass) | suite = unittest.TestLoader().loadTestsFromTestCase(TestJIDClass) | ||||||
|   | |||||||
| @@ -56,4 +56,21 @@ class TestErrorStanzas(SleekTest): | |||||||
|           </message> |           </message> | ||||||
|         """, use_values=False) |         """, use_values=False) | ||||||
|  |  | ||||||
|  |     def testDelText(self): | ||||||
|  |         """Test deleting the text of an error.""" | ||||||
|  |         msg = self.Message() | ||||||
|  |         msg['error']['test'] = 'Error!' | ||||||
|  |         msg['error']['condition'] = 'internal-server-error' | ||||||
|  |  | ||||||
|  |         del msg['error']['text'] | ||||||
|  |  | ||||||
|  |         self.check_message(msg, """ | ||||||
|  |           <message type="error"> | ||||||
|  |             <error type="cancel"> | ||||||
|  |               <internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||||
|  |             </error> | ||||||
|  |           </message> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |  | ||||||
| suite = unittest.TestLoader().loadTestsFromTestCase(TestErrorStanzas) | suite = unittest.TestLoader().loadTestsFromTestCase(TestErrorStanzas) | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								tests/test_stream_exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								tests/test_stream_exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | import sys | ||||||
|  | import sleekxmpp | ||||||
|  | from sleekxmpp.exceptions import XMPPError | ||||||
|  | from sleekxmpp.test import * | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestStreamExceptions(SleekTest): | ||||||
|  |     """ | ||||||
|  |     Test handling roster updates. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.stream_close() | ||||||
|  |  | ||||||
|  |     def testXMPPErrorException(self): | ||||||
|  |         """Test raising an XMPPError exception.""" | ||||||
|  |  | ||||||
|  |         def message(msg): | ||||||
|  |             raise XMPPError(condition='feature-not-implemented', | ||||||
|  |                             text="We don't do things that way here.", | ||||||
|  |                             etype='cancel', | ||||||
|  |                             extension='foo', | ||||||
|  |                             extension_ns='foo:error', | ||||||
|  |                             extension_args={'test': 'true'}) | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('message', message) | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <message> | ||||||
|  |             <body>This is going to cause an error.</body> | ||||||
|  |           </message> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         self.stream_send_message(""" | ||||||
|  |           <message 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"> | ||||||
|  |                 We don't do things that way here. | ||||||
|  |               </text> | ||||||
|  |               <foo xmlns="foo:error" test="true" /> | ||||||
|  |             </error> | ||||||
|  |           </message> | ||||||
|  |         """, use_values=False) | ||||||
|  |  | ||||||
|  |     def testThreadedXMPPErrorException(self): | ||||||
|  |         """Test raising an XMPPError exception in a threaded handler.""" | ||||||
|  |  | ||||||
|  |         def message(msg): | ||||||
|  |             raise XMPPError(condition='feature-not-implemented', | ||||||
|  |                             text="We don't do things that way here.", | ||||||
|  |                             etype='cancel') | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('message', message, | ||||||
|  |                                     threaded=True) | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <message> | ||||||
|  |             <body>This is going to cause an error.</body> | ||||||
|  |           </message> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         self.stream_send_message(""" | ||||||
|  |           <message 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"> | ||||||
|  |                 We don't do things that way here. | ||||||
|  |               </text> | ||||||
|  |             </error> | ||||||
|  |           </message> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |     def testUnknownException(self): | ||||||
|  |         """Test raising an generic exception in a threaded handler.""" | ||||||
|  |  | ||||||
|  |         def message(msg): | ||||||
|  |             raise ValueError("Did something wrong") | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('message', message) | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <message> | ||||||
|  |             <body>This is going to cause an error.</body> | ||||||
|  |           </message> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         if sys.version_info < (3, 0): | ||||||
|  |             self.stream_send_message(""" | ||||||
|  |               <message type="error"> | ||||||
|  |                 <error type="cancel"> | ||||||
|  |                   <undefined-condition | ||||||
|  |                       xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> | ||||||
|  |                   <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> | ||||||
|  |                     SleekXMPP got into trouble. | ||||||
|  |                   </text> | ||||||
|  |                 </error> | ||||||
|  |               </message> | ||||||
|  |             """) | ||||||
|  |         else: | ||||||
|  |             # Unfortunately, tracebacks do not make for very portable tests. | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamExceptions) | ||||||
							
								
								
									
										188
									
								
								tests/test_stream_presence.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								tests/test_stream_presence.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | |||||||
|  | import time | ||||||
|  | from sleekxmpp.test import * | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestStreamPresence(SleekTest): | ||||||
|  |     """ | ||||||
|  |     Test handling roster updates. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.stream_close() | ||||||
|  |  | ||||||
|  |     def testInitialUnavailablePresences(self): | ||||||
|  |         """ | ||||||
|  |         Test receiving unavailable presences from JIDs that | ||||||
|  |         are not online. | ||||||
|  |         """ | ||||||
|  |         events = set() | ||||||
|  |  | ||||||
|  |         def got_offline(presence): | ||||||
|  |             # The got_offline event should not be triggered. | ||||||
|  |             events.add('got_offline') | ||||||
|  |  | ||||||
|  |         def unavailable(presence): | ||||||
|  |             # The presence_unavailable event should be triggered. | ||||||
|  |             events.add('unavailable') | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('got_offline', got_offline) | ||||||
|  |         self.xmpp.add_event_handler('presence_unavailable', unavailable) | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence type="unavailable" from="otheruser@localhost" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Give event queue time to process. | ||||||
|  |         time.sleep(0.1) | ||||||
|  |  | ||||||
|  |         self.assertEqual(events, set(('unavailable',)), | ||||||
|  |                 "Got offline incorrectly triggered: %s." % events) | ||||||
|  |  | ||||||
|  |     def testGotOffline(self): | ||||||
|  |         """Test that got_offline is triggered properly.""" | ||||||
|  |         events = [] | ||||||
|  |  | ||||||
|  |         def got_offline(presence): | ||||||
|  |             events.append('got_offline') | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('got_offline', got_offline) | ||||||
|  |  | ||||||
|  |         # Setup roster. Use a 'set' instead of 'result' so we | ||||||
|  |         # don't have to handle get_roster() blocking. | ||||||
|  |         # | ||||||
|  |         # We use the stream to initialize the roster to make | ||||||
|  |         # the test independent of the roster implementation. | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <iq type="set"> | ||||||
|  |             <query xmlns="jabber:iq:roster"> | ||||||
|  |               <item jid="otheruser@localhost" | ||||||
|  |                     name="Other User" | ||||||
|  |                     subscription="both"> | ||||||
|  |                 <group>Testers</group> | ||||||
|  |               </item> | ||||||
|  |             </query> | ||||||
|  |           </iq> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Contact comes online. | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence from="otheruser@localhost/foobar" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Contact goes offline, should trigger got_offline. | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence from="otheruser@localhost/foobar" | ||||||
|  |                     type="unavailable" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Give event queue time to process. | ||||||
|  |         time.sleep(0.1) | ||||||
|  |  | ||||||
|  |         self.assertEqual(events, ['got_offline'], | ||||||
|  |                 "Got offline incorrectly triggered: %s" % events) | ||||||
|  |  | ||||||
|  |     def testGotOnline(self): | ||||||
|  |         """Test that got_online is triggered properly.""" | ||||||
|  |  | ||||||
|  |         events = set() | ||||||
|  |  | ||||||
|  |         def presence_available(p): | ||||||
|  |             events.add('presence_available') | ||||||
|  |  | ||||||
|  |         def got_online(p): | ||||||
|  |             events.add('got_online') | ||||||
|  |  | ||||||
|  |         self.stream_start() | ||||||
|  |         self.xmpp.add_event_handler('presence_available', presence_available) | ||||||
|  |         self.xmpp.add_event_handler('got_online', got_online) | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence from="user@localhost" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Give event queue time to process. | ||||||
|  |         time.sleep(0.1) | ||||||
|  |  | ||||||
|  |         expected = set(('presence_available', 'got_online')) | ||||||
|  |         self.assertEqual(events, expected, | ||||||
|  |                 "Incorrect events triggered: %s" % events) | ||||||
|  |  | ||||||
|  |     def testAutoAuthorizeAndSubscribe(self): | ||||||
|  |         """ | ||||||
|  |         Test auto authorizing and auto subscribing | ||||||
|  |         to subscription requests. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         events = set() | ||||||
|  |  | ||||||
|  |         def presence_subscribe(p): | ||||||
|  |             events.add('presence_subscribe') | ||||||
|  |  | ||||||
|  |         def changed_subscription(p): | ||||||
|  |             events.add('changed_subscription') | ||||||
|  |  | ||||||
|  |         self.stream_start(jid='tester@localhost') | ||||||
|  |  | ||||||
|  |         self.xmpp.add_event_handler('changed_subscription', | ||||||
|  |                                     changed_subscription) | ||||||
|  |         self.xmpp.add_event_handler('presence_subscribe', | ||||||
|  |                                     presence_subscribe) | ||||||
|  |  | ||||||
|  |         # With these settings we should accept a subscription | ||||||
|  |         # and request a subscription in return. | ||||||
|  |         self.xmpp.auto_authorize = True | ||||||
|  |         self.xmpp.auto_subscribe = True | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence from="user@localhost" type="subscribe" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         self.stream_send_presence(""" | ||||||
|  |           <presence to="user@localhost" type="subscribed" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         self.stream_send_presence(""" | ||||||
|  |           <presence to="user@localhost" type="subscribe" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         expected = set(('presence_subscribe', 'changed_subscription')) | ||||||
|  |         self.assertEqual(events, expected, | ||||||
|  |                 "Incorrect events triggered: %s" % events) | ||||||
|  |  | ||||||
|  |     def testNoAutoAuthorize(self): | ||||||
|  |         """Test auto rejecting subscription requests.""" | ||||||
|  |  | ||||||
|  |         events = set() | ||||||
|  |  | ||||||
|  |         def presence_subscribe(p): | ||||||
|  |             events.add('presence_subscribe') | ||||||
|  |  | ||||||
|  |         def changed_subscription(p): | ||||||
|  |             events.add('changed_subscription') | ||||||
|  |  | ||||||
|  |         self.stream_start(jid='tester@localhost') | ||||||
|  |  | ||||||
|  |         self.xmpp.add_event_handler('changed_subscription', | ||||||
|  |                                     changed_subscription) | ||||||
|  |         self.xmpp.add_event_handler('presence_subscribe', | ||||||
|  |                                     presence_subscribe) | ||||||
|  |  | ||||||
|  |         # With this setting we should reject all subscriptions. | ||||||
|  |         self.xmpp.auto_authorize = False | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <presence from="user@localhost" type="subscribe" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         self.stream_send_presence(""" | ||||||
|  |           <presence to="user@localhost" type="unsubscribed" /> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         expected = set(('presence_subscribe', 'changed_subscription')) | ||||||
|  |         self.assertEqual(events, expected, | ||||||
|  |                 "Incorrect events triggered: %s" % events) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamPresence) | ||||||
							
								
								
									
										86
									
								
								tests/test_stream_roster.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tests/test_stream_roster.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | from sleekxmpp.test import * | ||||||
|  | import time | ||||||
|  | import threading | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestStreamRoster(SleekTest): | ||||||
|  |     """ | ||||||
|  |     Test handling roster updates. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.stream_close() | ||||||
|  |  | ||||||
|  |     def testGetRoster(self): | ||||||
|  |         """Test handling roster requests.""" | ||||||
|  |         self.stream_start(mode='client') | ||||||
|  |         self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.") | ||||||
|  |  | ||||||
|  |         # Since get_roster blocks, we need to run it in a thread. | ||||||
|  |         t = threading.Thread(name='get_roster', target=self.xmpp.get_roster) | ||||||
|  |         t.start() | ||||||
|  |  | ||||||
|  |         self.stream_send_iq(""" | ||||||
|  |           <iq type="get" id="1"> | ||||||
|  |             <query xmlns="jabber:iq:roster" /> | ||||||
|  |           </iq> | ||||||
|  |         """) | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <iq type="result" id="1"> | ||||||
|  |             <query xmlns="jabber:iq:roster"> | ||||||
|  |               <item jid="user@localhost" | ||||||
|  |                     name="User" | ||||||
|  |                     subscription="both"> | ||||||
|  |                 <group>Friends</group> | ||||||
|  |                 <group>Examples</group> | ||||||
|  |               </item> | ||||||
|  |             </query> | ||||||
|  |           </iq> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         # Wait for get_roster to return. | ||||||
|  |         t.join() | ||||||
|  |  | ||||||
|  |         roster = {'user@localhost': {'name': 'User', | ||||||
|  |                                      'subscription': 'both', | ||||||
|  |                                      'groups': ['Friends', 'Examples'], | ||||||
|  |                                      'presence': {}, | ||||||
|  |                                      'in_roster': True}} | ||||||
|  |         self.failUnless(self.xmpp.roster == roster, | ||||||
|  |                 "Unexpected roster values: %s" % self.xmpp.roster) | ||||||
|  |  | ||||||
|  |     def testRosterSet(self): | ||||||
|  |         """Test handling pushed roster updates.""" | ||||||
|  |         self.stream_start(mode='client') | ||||||
|  |         self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.") | ||||||
|  |  | ||||||
|  |         self.stream_recv(""" | ||||||
|  |           <iq type="set" id="1"> | ||||||
|  |             <query xmlns="jabber:iq:roster"> | ||||||
|  |               <item jid="user@localhost" | ||||||
|  |                     name="User" | ||||||
|  |                     subscription="both"> | ||||||
|  |                 <group>Friends</group> | ||||||
|  |                 <group>Examples</group> | ||||||
|  |               </item> | ||||||
|  |             </query> | ||||||
|  |           </iq> | ||||||
|  |         """) | ||||||
|  |         self.stream_send_iq(""" | ||||||
|  |           <iq type="result" id="1"> | ||||||
|  |             <query xmlns="jabber:iq:roster" /> | ||||||
|  |           </iq> | ||||||
|  |         """) | ||||||
|  |  | ||||||
|  |         roster = {'user@localhost': {'name': 'User', | ||||||
|  |                                      'subscription': 'both', | ||||||
|  |                                      'groups': ['Friends', 'Examples'], | ||||||
|  |                                      'presence': {}, | ||||||
|  |                                      'in_roster': True}} | ||||||
|  |         self.failUnless(self.xmpp.roster == roster, | ||||||
|  |                 "Unexpected roster values: %s" % self.xmpp.roster) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster) | ||||||
| @@ -73,6 +73,16 @@ class TestToString(SleekTest): | |||||||
|             message="The xmlns parameter was not used properly.", |             message="The xmlns parameter was not used properly.", | ||||||
|             xmlns='foo') |             xmlns='foo') | ||||||
|  |  | ||||||
|  |     def testTailContent(self): | ||||||
|  |         """ | ||||||
|  |         Test that elements of the form <a>foo <b>bar</b> baz</a> only | ||||||
|  |         include " baz" once. | ||||||
|  |         """ | ||||||
|  |         self.tryTostring( | ||||||
|  |             original='<a>foo <b>bar</b> baz</a>', | ||||||
|  |             message='Element tail content is incorrect.') | ||||||
|  |  | ||||||
|  |  | ||||||
|     def testStanzaNs(self): |     def testStanzaNs(self): | ||||||
|         """ |         """ | ||||||
|         Test using the stanza_ns tostring parameter, which will prevent |         Test using the stanza_ns tostring parameter, which will prevent | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user