From a757177e452f869938583014649266d804dfcb79 Mon Sep 17 00:00:00 2001
From: Stephan Erb <steve-e@h3c.de>
Date: Sun, 11 Jan 2009 13:49:03 +0000
Subject: [PATCH] Improve code coverage of our testsuites and do some
 refactoring.

 * resolver does not depend on GTK anymore
 * renamed a few modules for consistency
 * moved all mocks to lib/
 * let client_nb test work again. Was broken here

There are many failing tests, help appreciated :-)
---
 test/lib/__init__.py                          |  10 +-
 test/lib/data.py                              |   2 +
 test/lib/{mocks.py => gajim_mocks.py}         |   6 +-
 test/{ => lib}/xmpp_mocks.py                  |  58 ++--
 test/runtests.py                              |  12 +-
 test/test_caps.py                             |   4 +-
 test/test_dispatcher_nb.py                    |  55 ---
 test/test_misc_interface.py                   |   6 +-
 test/test_nonblockingtcp.py                   |  84 -----
 test/test_resolver.py                         |  57 +--
 test/test_roster.py                           |   4 +-
 test/test_sessions.py                         |   4 +-
 ...st_client_nb.py => test_xmpp_client_nb.py} |  64 ++--
 test/test_xmpp_dispatcher_nb.py               |  98 ++++++
 test/test_xmpp_transports_nb.py               | 324 ++++++++++++++++++
 15 files changed, 535 insertions(+), 253 deletions(-)
 rename test/lib/{mocks.py => gajim_mocks.py} (98%)
 rename test/{ => lib}/xmpp_mocks.py (64%)
 delete mode 100644 test/test_dispatcher_nb.py
 delete mode 100644 test/test_nonblockingtcp.py
 rename test/{test_client_nb.py => test_xmpp_client_nb.py} (73%)
 create mode 100644 test/test_xmpp_dispatcher_nb.py
 create mode 100644 test/test_xmpp_transports_nb.py

diff --git a/test/lib/__init__.py b/test/lib/__init__.py
index 2b99a7e230..4b5f8ca12c 100644
--- a/test/lib/__init__.py
+++ b/test/lib/__init__.py
@@ -1,5 +1,5 @@
 import sys
-import os.path
+import os
 import getopt
 
 use_x = True
@@ -12,7 +12,8 @@ for o, a in opts:
 
 gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../..')
 
-# look for modules in the CWD, then gajim/test/lib, then gajim/src, then everywhere else
+# look for modules in the CWD, then gajim/test/lib, then gajim/src,
+# then everywhere else
 sys.path.insert(1, gajim_root + '/src')
 sys.path.insert(1, gajim_root + '/test/lib')
 
@@ -23,8 +24,6 @@ configdir = gajim_root + '/test/tmp'
 import __builtin__
 __builtin__._ = lambda x: x
 
-import os
-
 def setup_env():
 	# wipe config directory
 	if os.path.isdir(configdir):
@@ -40,6 +39,9 @@ def setup_env():
 	# for some reason common.gajim needs to be imported before xmpppy?
 	from common import gajim
 
+	import logging
+	logging.basicConfig()
+
 	gajim.DATA_DIR = gajim_root + '/data'
 	gajim.use_x = use_x
 
diff --git a/test/lib/data.py b/test/lib/data.py
index e74e9c01b0..c713de0acf 100755
--- a/test/lib/data.py
+++ b/test/lib/data.py
@@ -52,6 +52,7 @@ contacts[account3] = {
 	#		  'ask': None, 'groups': [], 'name': None,
 	#		  'resources': {}, 'subscription': u'both'}
 }
+
 # We have contacts that are not in roster but only specified in the metadata
 metacontact_data = [
 	[{'account': account3,
@@ -75,3 +76,4 @@ metacontact_data = [
 	  'order': 0}]
 	]
 
+# vim: se ts=3:
diff --git a/test/lib/mocks.py b/test/lib/gajim_mocks.py
similarity index 98%
rename from test/lib/mocks.py
rename to test/lib/gajim_mocks.py
index a58d24de83..44b18f0376 100644
--- a/test/lib/mocks.py
+++ b/test/lib/gajim_mocks.py
@@ -1,6 +1,8 @@
-# gajim-specific mock objects
-from mock import Mock
+'''
+Module with dummy classes for Gajim specific unit testing
+'''
 
+from mock import Mock
 from common import gajim
 
 from common.connection_handlers import ConnectionHandlersBase
diff --git a/test/xmpp_mocks.py b/test/lib/xmpp_mocks.py
similarity index 64%
rename from test/xmpp_mocks.py
rename to test/lib/xmpp_mocks.py
index 701a4402b0..4b6b469ad3 100644
--- a/test/xmpp_mocks.py
+++ b/test/lib/xmpp_mocks.py
@@ -2,25 +2,15 @@
 Module with dummy classes for unit testing of XMPP and related code.
 '''
 
-import threading, time, os.path, sys
-
-gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
-sys.path.append(gajim_root + '/src/common/xmpp')
-import idlequeue
-from client import PlugIn
-
-import lib
-lib.setup_env()
-
+import threading, time
 
 from mock import Mock
 
+from common.xmpp import idlequeue
+from common.xmpp.plugin import PlugIn
 
-
-idlequeue_interval = 0.2
-'''
-IdleQueue polling interval. 200ms is used in Gajim as default
-'''
+IDLEQUEUE_INTERVAL = 0.2 # polling interval. 200ms is used in Gajim as default
+IDLEMOCK_TIMEOUT = 30 # how long we wait for an event
 
 class IdleQueueThread(threading.Thread):
 	'''
@@ -28,18 +18,14 @@ class IdleQueueThread(threading.Thread):
 	'''
 	def __init__(self):
 		self.iq = idlequeue.IdleQueue()
-		self.stop = threading.Event()
-		'''
-		Event used to stopping the thread main loop.
-		'''
-
+		self.stop = threading.Event() # Event to stop the thread main loop.
 		self.stop.clear()
 		threading.Thread.__init__(self)
 	
 	def run(self):
 		while not self.stop.isSet():
 			self.iq.process()
-			time.sleep(idlequeue_interval)
+			time.sleep(IDLEQUEUE_INTERVAL)
 
 	def stop_thread(self):
 		self.stop.set()
@@ -51,25 +37,27 @@ class IdleMock:
 	Allows to wait for asynchronous callbacks with wait() method. 
 	'''
 	def __init__(self):
-		self.event = threading.Event()
-		'''
-		Event is used for waiting on callbacks.
-		'''
-		self.event.clear()
+		self._event = threading.Event()
+		self._event.clear()
 
 	def wait(self):
 		'''
-		Waiting until some callback sets the event and clearing the event
+		Block until some callback sets the event and clearing the event
 		subsequently. 
+		Returns True if event was set, False on timeout
 		'''
-		self.event.wait()
-		self.event.clear()
+		self._event.wait(IDLEMOCK_TIMEOUT)
+		if self._event.is_set():
+			self._event.clear()
+			return True
+		else:
+			return False
 
 	def set_event(self):
-		self.event.set()
+		self._event.set()
 
 
-class MockConnectionClass(IdleMock, Mock):
+class MockConnection(IdleMock, Mock):
 	'''
 	Class simulating Connection class from src/common/connection.py
 
@@ -88,12 +76,9 @@ class MockConnectionClass(IdleMock, Mock):
 		Method called after connecting - after receiving <stream:features>
 		from server (NOT after TLS stream restart) or connect failure
 		'''
-		#print 'on_connect - args:'
-		#print '    success - %s' % success
-		#for i in args:
-		#	print '    %s' % i
 		self.connect_succeeded = success
 		self.set_event()
+		
 
 	def on_auth(self, con, auth):
 		'''
@@ -106,9 +91,6 @@ class MockConnectionClass(IdleMock, Mock):
 				type of authetication in case of success ('old_auth', 'sasl') or
 				None in case of auth failure
 		'''
-		#print 'on_auth - args:'
-		#print '    con: %s' % con
-		#print '    auth: %s' % auth
 		self.auth_connection = con
 		self.auth = auth
 		self.set_event()
diff --git a/test/runtests.py b/test/runtests.py
index 3566653e48..a865f78066 100755
--- a/test/runtests.py
+++ b/test/runtests.py
@@ -35,16 +35,18 @@ for o, a in opts:
 			sys.exit(2)
 
 # new test modules need to be added manually
+#modules = ( 'test_xmpp_dispatcher_nb',
+#				'test_xmpp_client_nb',
+#				'test_xmpp_transports_nb',
+#				'test_resolver',
+#				'test_sessions',
+#				'test_caps',
+#			 )
 modules = ()
 
 if use_x:
 	modules += ('test_misc_interface',
 					'test_roster',
-					'test_sessions',
-					'test_resolver',
-					'test_caps',
-					'test_dispatcher_nb',
-					'test_nonblockingtcp',
 	)
 
 nb_errors = 0
diff --git a/test/test_caps.py b/test/test_caps.py
index 34bd6abe19..37b24007f3 100644
--- a/test/test_caps.py
+++ b/test/test_caps.py
@@ -1,4 +1,6 @@
-# tests for capabilities and the capabilities cache
+'''
+Tests for capabilities and the capabilities cache
+'''
 import unittest
 
 import lib
diff --git a/test/test_dispatcher_nb.py b/test/test_dispatcher_nb.py
deleted file mode 100644
index 9af6717b8a..0000000000
--- a/test/test_dispatcher_nb.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# tests for xmpppy's dispatcher_nb.py
-import unittest
-
-import lib
-lib.setup_env()
-
-from mock import Mock
-
-from common.xmpp import dispatcher_nb
-from common.xmpp import auth_nb
-
-class TestDispatcherNB(unittest.TestCase):
-	def test_unbound_namespace_prefix(self):
-		'''tests our handling of a message with an unbound namespace prefix'''
-		d = dispatcher_nb.XMPPDispatcher()
-
-		conn = Mock()
-
-		owner = Mock()
-		owner._caller = Mock()
-		owner.defaultNamespace = auth_nb.NS_CLIENT
-		owner.debug_flags = []
-		owner.Connection = conn
-		owner._component = False
-
-		d._owner = owner
-		d.plugin(owner)
-
-		msgs = []
-
-		def _got_message(conn, msg):
-			msgs.append(msg)
-
-		d.RegisterHandler('message', _got_message)
-
-		d.StreamInit()
-
-		d.ProcessNonBlocking("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>")
-
-		# should be able to parse a normal message
-		d.ProcessNonBlocking('<message><body>hello</body></message>')
-		self.assertEqual(1, len(msgs))
-
-		d.ProcessNonBlocking('<message><x:y/></message>')
-		# we should not have been disconnected after that message
-		self.assertEqual(0, len(conn.mockGetNamedCalls('pollend')))
-
-		# we should be able to keep parsing
-		d.ProcessNonBlocking('<message><body>still here?</body></message>')
-		self.assertEqual(3, len(msgs))
-
-if __name__ == '__main__':
-	unittest.main()
-
-# vim: se ts=3:
diff --git a/test/test_misc_interface.py b/test/test_misc_interface.py
index 7398fb6194..4e31b35dc9 100644
--- a/test/test_misc_interface.py
+++ b/test/test_misc_interface.py
@@ -1,4 +1,6 @@
-# tests for the miscellaneous functions scattered throughout src/gajim.py
+'''
+Tests for the miscellaneous functions scattered throughout src/gajim.py
+'''
 import unittest
 
 import lib
@@ -7,7 +9,7 @@ lib.setup_env()
 from common import gajim
 from gajim import Interface
 
-from mocks import *
+from gajim_mocks import *
 gajim.logger = MockLogger()
 
 Interface()
diff --git a/test/test_nonblockingtcp.py b/test/test_nonblockingtcp.py
deleted file mode 100644
index 47f20448a9..0000000000
--- a/test/test_nonblockingtcp.py
+++ /dev/null
@@ -1,84 +0,0 @@
-'''
-Unit test for NonBlockingTCP tranport.
-'''
-
-import unittest
-from xmpp_mocks import IdleQueueThread, IdleMock
-import sys, os.path
-
-gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
-
-sys.path.append(gajim_root + '/src/common/xmpp')
-sys.path.append(gajim_root + '/src/common')
-
-import transports_nb
-from client import *
-
-xmpp_server = ('gajim.org', 5222)
-'''
-2-tuple  - (XMPP server hostname, c2s port)
-Script will connect to the machine.
-'''
-
-import socket
-ips = socket.getaddrinfo(xmpp_server[0], xmpp_server[1],
-	socket.AF_UNSPEC,socket.SOCK_STREAM) 
-
-# change xmpp_server on real values
-ip = ips[0]
-
-class MockClient(IdleMock):
-
-	def __init__(self, idlequeue):
-		self.idlequeue = idlequeue
-		IdleMock.__init__(self)
-
-	def do_connect(self):
-		self.socket = transports_nb.NonBlockingTCP(
-			lambda(event_type, data): sys.stdout.write('raising event %s: %s' % (
-			event_type, data)), lambda: self.on_success(mode='SocketDisconnect'),
-			self.idlequeue, False, None)
-
-		self.socket.PlugIn(self)
-
-		self.socket.connect(conn_5tuple=ip,
-			on_connect=lambda: self.on_success(mode='TCPconnect'),
-			on_connect_failure=self.on_failure)
-		self.wait()
-
-	def do_disconnect(self):
-		self.socket.disconnect()
-		self.wait()
-
-	def on_failure(self, data):
-		print 'Error: %s' % data
-		self.set_event()
-
-	def on_success(self, mode, data=None):
-		if mode == "TCPconnect":
-			pass
-		if mode == "SocketDisconnect":
-			pass
-		self.set_event()
-
-class TestNonBlockingTCP(unittest.TestCase):
-	def setUp(self):
-		self.idlequeue_thread = IdleQueueThread()
-		self.idlequeue_thread.start()
-		self.client = MockClient(idlequeue=self.idlequeue_thread.iq)
-
-	def tearDown(self):
-		self.idlequeue_thread.stop_thread()
-		self.idlequeue_thread.join()
-
-	def test_connect_disconnect(self):
-		self.client.do_connect()
-		self.assert_(self.client.socket.state == 'CONNECTED')
-		self.client.do_disconnect()
-		self.assert_(self.client.socket.state == 'DISCONNECTED')
-
-
-if __name__ == '__main__':
-	unittest.main()
-
-# vim: se ts=3:
diff --git a/test/test_resolver.py b/test/test_resolver.py
index 39794004bb..69312c3dee 100644
--- a/test/test_resolver.py
+++ b/test/test_resolver.py
@@ -6,12 +6,10 @@ import lib
 lib.setup_env()
 
 from common import resolver
-from common.xmpp.idlequeue import GlibIdleQueue
 
 from mock import Mock, expectParams
-from mocks import *
-
-import gtk
+from gajim_mocks import *
+from xmpp_mocks import IdleQueueThread
 
 GMAIL_SRV_NAME = '_xmpp-client._tcp.gmail.com'
 NONSENSE_NAME = 'sfsdfsdfsdf.sdfs.fsd'
@@ -23,21 +21,30 @@ TEST_LIST = [(GMAIL_SRV_NAME, 'srv', True),
 	(JABBERCZ_SRV_NAME, 'srv', True)]
 
 class TestResolver(unittest.TestCase):
-	''' Test for LibAsyncNSResolver and NSLookupResolver '''
-
+	'''
+	Test for LibAsyncNSResolver and NSLookupResolver. Requires working
+	network connection.
+	'''
 	def setUp(self):
-		self.iq = GlibIdleQueue()
-		self.reset()
+		self.idlequeue_thread = IdleQueueThread()
+		self.idlequeue_thread.start()
+
+		self.iq = self.idlequeue_thread.iq
+		self._reset()
 		self.resolver = None
+	
+	def tear_down(self):
+		self.idlequeue_thread.stop_thread()
+		self.idlequeue_thread.join()
 
-	def reset(self):
+	def _reset(self):
 		self.flag = False
 		self.expect_results = False
 		self.nslookup = False
 		self.resolver = None
 
 	def testLibAsyncNSResolver(self):
-		self.reset()
+		self._reset()
 		if not resolver.USE_LIBASYNCNS:
 			print 'testLibAsyncResolver: libasyncns-python not installed'
 			return
@@ -45,51 +52,49 @@ class TestResolver(unittest.TestCase):
 
 		for name, type, expect_results in TEST_LIST:
 			self.expect_results = expect_results
-			self.runLANSR(name, type)
+			self._runLANSR(name, type)
 			self.flag = False
 
-	def runLANSR(self, name, type):
+	def _runLANSR(self, name, type):
 		self.resolver.resolve(
 			host = name,
 			type = type,
-			on_ready = self.myonready)
+			on_ready = self._myonready)
 		while not self.flag:
 			time.sleep(1)
 			self.resolver.process()
 
-	def myonready(self, name, result_set):
+	def _myonready(self, name, result_set):
 		if __name__ == '__main__':
-			print 'on_ready called ...'
-			print 'hostname: %s' % name
-			print 'result set: %s' % result_set
-			print 'res.resolved_hosts: %s' % self.resolver.resolved_hosts
+			from pprint import pprint
+			pprint('on_ready called ...')
+			pprint('hostname: %s' % name)
+			pprint('result set: %s' % result_set)
+			pprint('res.resolved_hosts: %s' % self.resolver.resolved_hosts)
+			pprint('')
 		if self.expect_results:
 			self.assert_(len(result_set) > 0)
 		else:
 			self.assert_(result_set == [])
 		self.flag = True
-		if self.nslookup: self._testNSLR()
+		if self.nslookup:
+			self._testNSLR()
 		
 	def testNSLookupResolver(self):
-		self.reset()
+		self._reset()
 		self.nslookup = True
 		self.resolver = resolver.NSLookupResolver(self.iq)
 		self.test_list = TEST_LIST
 		self._testNSLR()
-		try:
-			gtk.main()
-		except KeyboardInterrupt:
-			print 'KeyboardInterrupt caught'
 
 	def _testNSLR(self):
 		if self.test_list == []: 
-			gtk.main_quit()
 			return
 		name, type, self.expect_results = self.test_list.pop()
 		self.resolver.resolve(
 			host = name,
 			type = type,
-			on_ready = self.myonready)
+			on_ready = self._myonready)
 
 if __name__ == '__main__':
 	unittest.main()
diff --git a/test/test_roster.py b/test/test_roster.py
index 421148c831..0172efa361 100644
--- a/test/test_roster.py
+++ b/test/test_roster.py
@@ -1,14 +1,12 @@
 import unittest
 
-import time
-
 import lib
 lib.setup_env()
 
 from data import *
 
 from mock import Mock, expectParams
-from mocks import *
+from gajim_mocks import *
 
 from common import gajim
 from common import zeroconf
diff --git a/test/test_sessions.py b/test/test_sessions.py
index 215949c6eb..a28b9259b9 100644
--- a/test/test_sessions.py
+++ b/test/test_sessions.py
@@ -9,7 +9,7 @@ from common import gajim
 from common import xmpp
 
 from mock import Mock, expectParams
-from mocks import *
+from gajim_mocks import *
 
 from common.stanza_session import StanzaSession
 
@@ -17,6 +17,7 @@ from common.stanza_session import StanzaSession
 account_name = 'test'
 
 class TestStanzaSession(unittest.TestCase):
+	''' Testclass for common/stanzasession.py '''
 	def setUp(self):
 		self.jid = 'test@example.org/Gajim'
 		self.conn = MockConnection(account_name, {'send_stanza': None})
@@ -74,6 +75,7 @@ gajim.interface = MockInterface()
 import notify
 
 class TestChatControlSession(unittest.TestCase):
+	''' Testclass for session.py '''
 	def setUp(self):
 		self.jid = 'test@example.org/Gajim'
 		self.conn = MockConnection(account_name, {'send_stanza': None})
diff --git a/test/test_client_nb.py b/test/test_xmpp_client_nb.py
similarity index 73%
rename from test/test_client_nb.py
rename to test/test_xmpp_client_nb.py
index 8e77927faf..b970204175 100644
--- a/test/test_client_nb.py
+++ b/test/test_xmpp_client_nb.py
@@ -1,24 +1,24 @@
 '''
 Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py)
-It actually connects to a xmpp server so the connection values have to be
-changed before running.
+
+It actually connects to a xmpp server.
 '''
 
 import unittest
-from xmpp_mocks import MockConnectionClass, IdleQueueThread
 
-import sys, os.path
+import lib
+lib.setup_env()
 
-gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
-sys.path.append(gajim_root + '/src/common/xmpp')
+from xmpp_mocks import MockConnection, IdleQueueThread
+from mock import Mock
+from common.xmpp import client_nb
 
-import client_nb
 
 # (XMPP server hostname, c2s port). Script will connect to the machine.
 xmpp_server_port = ('gajim.org', 5222)
 
 # [username, password, passphrase]. Script will authenticate to server above
-credentials = ['loginn', 'passwo', 'testresour']
+credentials = ['unittest', 'testtest', 'res']
 
 class TestNonBlockingClient(unittest.TestCase):
 	'''
@@ -27,7 +27,7 @@ class TestNonBlockingClient(unittest.TestCase):
 	def setUp(self):
 		''' IdleQueue thread is run and dummy connection is created. '''
 		self.idlequeue_thread = IdleQueueThread()
-		self.connection = MockConnectionClass()
+		self.connection = MockConnection() # for dummy callbacks
 		self.idlequeue_thread.start()
 
 	def tearDown(self):
@@ -35,6 +35,8 @@ class TestNonBlockingClient(unittest.TestCase):
 		self.idlequeue_thread.stop_thread()
 		self.idlequeue_thread.join()
 
+		self.client = None
+
 	def open_stream(self, server_port):
 		'''
 		Method opening the XMPP connection. It returns when <stream:features>
@@ -43,13 +45,11 @@ class TestNonBlockingClient(unittest.TestCase):
 		:param server_port: tuple of (hostname, port) for where the client should
 		connect.
 		'''
+
 		self.client = client_nb.NonBlockingClient(
 			domain=server_port[0],
-			idlequeue=self.idlequeue_thread.iq)
-		'''
-		NonBlockingClient instance with parameters from global variables and with
-		callbacks from dummy connection.
-		'''
+			idlequeue=self.idlequeue_thread.iq,
+			caller=Mock())
 
 		self.client.connect(
 			hostname=server_port[0],
@@ -58,8 +58,8 @@ class TestNonBlockingClient(unittest.TestCase):
 			on_connect_failure=lambda *args: self.connection.on_connect(
 				False, *args))
 
-		print 'waiting for callback from client constructor'
-		self.connection.wait()
+		self.assert_(self.connection.wait(),
+			msg='waiting for callback from client constructor')
 		
 		# if on_connect was called, client has to be connected and vice versa
 		if self.connection.connect_succeeded:
@@ -71,26 +71,26 @@ class TestNonBlockingClient(unittest.TestCase):
 		'''
 		Method authenticating connected client with supplied credentials. Returns
 		when authentication is over.
-		:param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication
 
-		:todo: to check and be more specific about when it returns (bind, session..)
+		:param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication
+		:todo: to check and be more specific about when it returns
+			(bind, session..)
 		'''
 		self.client.auth(username, password, resource, sasl, 
 				on_auth=self.connection.on_auth)
 
-		print 'waiting for authentication...'
-		self.connection.wait()
+		self.assert_(self.connection.wait(), msg='waiting for authentication')
 
 	def do_disconnect(self):
 		'''
-		Does disconnecting of connected client. Returns when TCP connection is closed.
+		Does disconnecting of connected client. Returns when TCP connection is 
+		closed.
 		'''
 		#self.client.start_disconnect(None, on_disconnect=self.connection.set_event)
 		self.client.RegisterDisconnectHandler(self.connection.set_event)
 		self.client.disconnect()
 
-		print 'waiting for disconnecting...'
-		self.connection.wait()
+		self.assertTrue(self.connection.wait(), msg='waiting for disconnecting')
 
 	def test_proper_connect_sasl(self):
 		'''
@@ -101,16 +101,15 @@ class TestNonBlockingClient(unittest.TestCase):
 
 		# if client is not connected, lets raise the AssertionError
 		self.assert_(self.client.get_connect_type())
-		# (client.disconnect() is already called from NBClient._on_connected_failure
-		# so there's need to call it in this case
+		# client.disconnect() is already called from NBClient via 
+		# _on_connected_failure, no need to call it here
 
 		self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1)
 		self.assert_(self.connection.con)
-		self.assert_(self.connection.auth=='sasl')
+		self.assert_(self.connection.auth=='sasl', msg="Unable to auth via SASL")
 
 		self.do_disconnect()
 
-
 	def test_proper_connect_oldauth(self):
 		'''
 		The ideal testcase - client is connected, authenticated with old auth and 
@@ -120,21 +119,22 @@ class TestNonBlockingClient(unittest.TestCase):
 		self.assert_(self.client.get_connect_type())
 		self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0)
 		self.assert_(self.connection.con)
-		self.assert_(self.connection.auth=='old_auth')
+		self.assert_(self.connection.auth=='old_auth',
+			msg="Unable to auth via old_auth")
 		self.do_disconnect()
 
 	def test_connect_to_nonexisting_host(self):
 		'''
-		Connect to nonexisting host. DNS request for A records should return nothing.
+		Connect to nonexisting host. DNS request for A records should return
+		nothing.
 		'''
 		self.open_stream(('fdsfsdf.fdsf.fss', 5222))
-		print 'nonexthost: %s' % self.client.get_connect_type()
 		self.assert_(not self.client.get_connect_type())
 
 	def test_connect_to_wrong_port(self):
 		'''
-		Connect to nonexisting host. DNS request for A records should return some IP
-		but there shouldn't be XMPP server running on specified port.
+		Connect to nonexisting server. DNS request for A records should return an
+		IP	but there shouldn't be XMPP server running on specified port.
 		'''
 		self.open_stream((xmpp_server_port[0], 31337))
 		self.assert_(not self.client.get_connect_type())
diff --git a/test/test_xmpp_dispatcher_nb.py b/test/test_xmpp_dispatcher_nb.py
new file mode 100644
index 0000000000..8665c21e71
--- /dev/null
+++ b/test/test_xmpp_dispatcher_nb.py
@@ -0,0 +1,98 @@
+'''
+Tests for dispatcher_nb.py
+'''
+import unittest
+
+import lib
+lib.setup_env()
+
+from mock import Mock
+
+from common.xmpp import dispatcher_nb
+from common.xmpp import protocol
+
+class TestDispatcherNB(unittest.TestCase):
+	'''
+	Test class for NonBlocking dispatcher. Tested dispatcher will be plugged
+	into a mock client
+	'''
+	def setUp(self):
+		self.dispatcher = dispatcher_nb.XMPPDispatcher()
+
+		# Setup mock client
+		self.client = Mock()
+		self.client.__str__ = lambda: 'Mock' # FIXME: why do I need this one?
+		self.client._caller = Mock()
+		self.client.defaultNamespace = protocol.NS_CLIENT
+		self.client.Connection = Mock() # mock transport
+		self.con = self.client.Connection
+	
+	def tearDown(self):
+		# Unplug if needed
+		if hasattr(self.dispatcher, '_owner'):
+			self.dispatcher.PlugOut()
+
+	def _simulate_connect(self):
+		self.dispatcher.PlugIn(self.client) # client is owner
+		# Simulate that we have established a connection
+		self.dispatcher.ProcessNonBlocking("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client'>")
+
+	def test_unbound_namespace_prefix(self):
+		'''tests our handling of a message with an unbound namespace prefix'''
+		self._simulate_connect()
+		
+		msgs = []
+		def _got_message(conn, msg):
+			msgs.append(msg)
+		self.dispatcher.RegisterHandler('message', _got_message)
+
+		# should be able to parse a normal message
+		self.dispatcher.ProcessNonBlocking('<message><body>hello</body></message>')
+		self.assertEqual(1, len(msgs))
+
+		self.dispatcher.ProcessNonBlocking('<message><x:y/></message>')
+		self.assertEqual(2, len(msgs))
+		# we should not have been disconnected after that message
+		self.assertEqual(0, len(self.con.mockGetNamedCalls('pollend')))
+		self.assertEqual(0, len(self.con.mockGetNamedCalls('disconnect')))
+
+		# we should be able to keep parsing
+		self.dispatcher.ProcessNonBlocking('<message><body>still here?</body></message>')
+		self.assertEqual(3, len(msgs))
+
+	def test_process_non_blocking(self):
+		''' Check for ProcessNonBlocking return types '''
+		self._simulate_connect()
+		process = self.dispatcher.ProcessNonBlocking
+
+		# length of data expected
+		data = "Please don't fail"
+		result = process(data)
+		self.assertEqual(result, len(data))
+
+		# no data processed, link shall still be active
+		result = process('')
+		self.assertEqual(result, '0')
+		self.assertEqual(0, len(self.con.mockGetNamedCalls('pollend')) + 
+			len(self.con.mockGetNamedCalls('disconnect')))
+
+		# simulate disconnect
+		result = process('</stream:stream>')
+		self.assertEqual(1, len(self.client.mockGetNamedCalls('disconnect')))
+
+	def test_return_stanza_handler(self):
+		''' Test sasl_error_conditions transformation in protocol.py '''
+		# quick'n dirty...I wasn't aware of it existance and thought it would
+		# always fail :-)
+		self._simulate_connect()
+		stanza = "<iq type='get' />"
+		def send(data):
+			self.assertEqual(str(data), '<iq xmlns="jabber:client" type="error"><error code="501" type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">The feature requested is not implemented by the recipient or server and therefore cannot be processed.</text></error></iq>')
+		self.client.send = send
+		self.dispatcher.ProcessNonBlocking(stanza)
+
+
+if __name__ == '__main__':
+	unittest.main()
+
+# vim: se ts=3:
diff --git a/test/test_xmpp_transports_nb.py b/test/test_xmpp_transports_nb.py
new file mode 100644
index 0000000000..2101526755
--- /dev/null
+++ b/test/test_xmpp_transports_nb.py
@@ -0,0 +1,324 @@
+'''
+Unit test for tranports classes.
+'''
+
+import unittest
+import socket
+
+import lib
+lib.setup_env()
+
+from xmpp_mocks import IdleQueueThread, IdleMock
+from common.xmpp import transports_nb
+
+
+class TestModuleLevelFunctions(unittest.TestCase):
+	'''
+	Test class for functions defined at module level
+	'''
+	def test_urisplit(self):
+		def check_uri(uri, proto, host, path):
+			_proto, _host, _path = transports_nb.urisplit(uri)
+			self.assertEqual(proto, _proto)
+			self.assertEqual(host, _host)
+			self.assertEqual(path, _path)
+		check_uri('http://httpcm.jabber.org/webclient',
+			proto='http', host='httpcm.jabber.org', path='/webclient')
+
+	def test_get_proxy_data_from_dict(self):
+		def check_dict(proxy_dict, host, port, user, passwd):
+			_host, _port, _user, _passwd = transports_nb.get_proxy_data_from_dict(
+				proxy_dict)
+			self.assertEqual(_host, host)
+			self.assertEqual(_port, port)
+			self.assertEqual(_user, user)
+			self.assertEqual(_passwd, passwd)
+
+		bosh_dict = {'bosh_content': u'text/xml; charset=utf-8',
+ 						'bosh_hold': 2,
+ 						'bosh_http_pipelining': False,
+ 						'bosh_port': 5280,
+ 						'bosh_uri': u'http://gajim.org:5280/http-bind',
+						'bosh_useproxy': False,
+ 						'bosh_wait': 30,
+ 						'bosh_wait_for_restart_response': False,
+ 						'host': u'172.16.99.11',
+ 						'pass': u'pass',
+ 						'port': 3128,
+ 						'type': u'bosh',
+ 						'useauth': True,
+ 						'user': u'user'}
+		check_dict(bosh_dict, host=u'gajim.org', port=80, user=u'user',
+			passwd=u'pass')
+
+		proxy_dict = {'bosh_content': u'text/xml; charset=utf-8',
+ 						'bosh_hold': 2,
+						'bosh_http_pipelining': False,
+						'bosh_port': 5280,
+						'bosh_uri': u'',
+						'bosh_useproxy': True,
+						'bosh_wait': 30,
+						'bosh_wait_for_restart_response': False,
+						'host': u'172.16.99.11',
+						'pass': u'pass',
+						'port': 3128,
+						'type': 'socks5',
+						'useauth': True,
+						'user': u'user'}
+		check_dict(proxy_dict, host=u'172.16.99.11', port=3128, user=u'user',
+			passwd=u'pass')
+
+
+class AbstractTransportTest(unittest.TestCase):
+	''' Encapsulates Idlequeue instantiation for transports and more...'''
+
+	def setUp(self):
+		''' IdleQueue thread is run and dummy connection is created. '''
+		self.idlequeue_thread = IdleQueueThread()
+		self.idlequeue_thread.start()
+		self._setup_hook()
+
+	def tearDown(self):
+		''' IdleQueue thread is stopped. '''
+		self._teardown_hook()
+		self.idlequeue_thread.stop_thread()
+		self.idlequeue_thread.join()
+
+	def _setup_hook(self):
+		pass
+
+	def _teardown_hook(self):
+		pass
+
+	def expect_receive(self, expected, count=1, msg=None):
+		'''
+		Returns a callback function that will assert whether the data passed to 
+		it equals the one specified when calling this function.
+
+		Can be used to make sure transport dispatch correct data.
+		'''
+		def receive(data, *args, **kwargs):
+			self.assertEqual(data, expected, msg=msg)
+			self._expected_count -= 1
+		self._expected_count = count
+		return receive
+
+	def have_received_expected(self):
+		'''
+		Plays together with expect_receive(). Will return true if expected_rcv
+		callback was called as often as specified
+		'''
+		return self._expected_count == 0
+
+
+class TestNonBlockingTCP(AbstractTransportTest):
+	'''
+	Test class for NonBlockingTCP. Will actually try to connect to an existing
+	XMPP server.
+	'''
+	class MockClient(IdleMock):
+		''' Simple client to test transport functionality '''
+		def __init__(self, idlequeue, testcase):
+			self.idlequeue = idlequeue
+			self.testcase = testcase
+			IdleMock.__init__(self)
+
+		def do_connect(self, establish_tls=False, proxy_dict=None):
+			try:
+				ips = socket.getaddrinfo('gajim.org', 5222,
+					socket.AF_UNSPEC,socket.SOCK_STREAM) 
+				ip = ips[0]
+			except socket.error, e:
+				self.testcase.fail(msg=str(e))
+
+			self.socket = transports_nb.NonBlockingTCP(
+				raise_event=lambda event_type, data: self.testcase.assertTrue(
+					event_type and data),
+				on_disconnect=lambda: self.on_success(mode='SocketDisconnect'),
+				idlequeue=self.idlequeue,
+				estabilish_tls=establish_tls,
+				certs=('../data/other/cacerts.pem', 'tmp/cacerts.pem'),
+				proxy_dict=proxy_dict)
+
+			self.socket.PlugIn(self)
+
+			self.socket.connect(conn_5tuple=ip,
+				on_connect=lambda: self.on_success(mode='TCPconnect'),
+				on_connect_failure=self.on_failure)
+			self.testcase.assertTrue(self.wait(), msg='Connection timed out')
+
+		def do_disconnect(self):
+			self.socket.disconnect()
+			self.testcase.assertTrue(self.wait(), msg='Disconnect timed out')
+
+		def on_failure(self, err_message):
+			self.set_event()
+			self.testcase.fail(msg=err_message)
+
+		def on_success(self, mode, data=None):
+			if mode == "TCPconnect":
+				pass
+			if mode == "SocketDisconnect":
+				pass
+			self.set_event()
+
+	def _setup_hook(self):
+		self.client = self.MockClient(idlequeue=self.idlequeue_thread.iq,
+			testcase=self)
+	
+	def _teardown_hook(self):
+		if self.client.socket.state == 'CONNECTED':
+			self.client.do_disconnect()
+
+	def test_connect_disconnect_plain(self):
+		''' Establish plain connection '''
+		self.client.do_connect(establish_tls=False)
+		self.assert_(self.client.socket.state == 'CONNECTED')
+		self.client.do_disconnect()
+		self.assert_(self.client.socket.state == 'DISCONNECTED')
+	
+	# FIXME: testcase not working...
+	#def test_connect_disconnect_ssl(self):
+	#	''' Establish SSL (not TLS) connection '''
+	#	self.client.do_connect(establish_tls=True)
+	#	self.assert_(self.client.socket.state == 'CONNECTED')
+	#	self.client.do_disconnect()
+	#	self.assert_(self.client.socket.state == 'DISCONNECTED')
+
+	def test_do_receive(self):
+		''' Test _do_receive method by overwriting socket.recv '''
+		self.client.do_connect()
+		sock = self.client.socket
+
+		# transport shall receive data
+		data = "Please don't fail"
+		sock._recv = lambda buffer: data
+		sock.onreceive(self.expect_receive(data))
+		sock._do_receive()
+		self.assertTrue(self.have_received_expected(), msg='Did not receive data')
+		self.assert_(self.client.socket.state == 'CONNECTED')
+
+		# transport shall do nothing as an non-fatal SSL is simulated
+		sock._recv = lambda buffer: None
+		sock.onreceive(self.assertFalse) # we did not receive anything...
+		sock._do_receive()
+		self.assert_(self.client.socket.state == 'CONNECTED')
+
+		# transport shall disconnect as remote side closed the connection 
+		sock._recv = lambda buffer: ''
+		sock.onreceive(self.assertFalse) # we did not receive anything...
+		sock._do_receive()
+		self.assert_(self.client.socket.state == 'DISCONNECTED')
+
+	def test_do_send(self):
+		''' Test _do_send method by overwriting socket.send '''
+		self.client.do_connect()
+		sock = self.client.socket
+
+		outgoing = [] # what we have actually send to our socket.socket
+		data_part1 = "Please don't "
+		data_part2 = "fail!"
+		data_complete = data_part1 + data_part2
+	
+		# Simulate everything could be send in one go
+		def _send_all(data):
+			outgoing.append(data)
+			return len(data)
+		sock._send = _send_all
+		sock.send(data_part1)
+		sock.send(data_part2)
+		sock._do_send()
+		sock._do_send()
+		self.assertTrue(self.client.socket.state == 'CONNECTED')
+		self.assertTrue(data_part1 in outgoing and data_part2 in outgoing)
+		self.assertFalse(sock.sendqueue and sock.sendbuff,
+			msg='There is still unsend data in buffers')
+
+		# Simulate data could only be sent in chunks
+		self.chunk_count = 0
+		outgoing = []
+		def _send_chunks(data):
+			if self.chunk_count == 0:
+				outgoing.append(data_part1)
+				self.chunk_count += 1
+				return len(data_part1)
+			else:
+				outgoing.append(data_part2)
+				return len(data_part2)
+		sock._send = _send_chunks
+		sock.send(data_complete)
+		sock._do_send() # process first chunk
+		sock._do_send() # process the second one
+		self.assertTrue(self.client.socket.state == 'CONNECTED')
+		self.assertTrue(data_part1 in outgoing and data_part2 in outgoing)
+		self.assertFalse(sock.sendqueue and sock.sendbuff,
+			msg='There is still unsend data in buffers')
+
+
+class TestNonBlockingHTTP(AbstractTransportTest):
+	''' Test class for NonBlockingHTTP transport'''
+
+	bosh_http_dict = {
+		'http_uri': 'http://httpcm.jabber.org/webclient',			
+		'http_port': 1010,
+		'http_version': 'HTTP/1.1',
+		'http_persistent': True,
+		'add_proxy_headers': False
+		}
+
+	def _get_transport(self, http_dict, proxy_dict=None):
+		return transports_nb.NonBlockingHTTP(
+			raise_event=None,
+			on_disconnect=None,
+			idlequeue=self.idlequeue_thread.iq,
+			estabilish_tls=False,
+			certs=None,
+			on_http_request_possible=lambda: None,
+			on_persistent_fallback=None,
+			http_dict=http_dict,
+			proxy_dict=proxy_dict,
+			)
+	
+	def test_parse_own_http_message(self):
+		''' Build a HTTP message and try to parse it afterwards '''
+		transport = self._get_transport(self.bosh_http_dict)
+
+		data = "<test>Please don't fail!</test>"
+		http_message = transport.build_http_message(data)
+		statusline, headers, http_body = transport.parse_http_message(
+			http_message)
+
+		self.assertTrue(statusline and isinstance(statusline, list))
+		self.assertTrue(headers and isinstance(headers, dict))
+		self.assertEqual(data, http_body, msg='Input and output are different')
+
+	def test_receive_http_message(self):
+		''' Let _on_receive handle some http messages '''
+		transport = self._get_transport(self.bosh_http_dict)
+		
+		header = ("HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n" +
+			"Content-Length: 88\r\n\r\n")
+		payload = "<test>Please don't fail!</test>"
+		body = "<body xmlns='http://jabber.org/protocol/httpbind'>%s</body>" \
+			% payload
+		message = "%s%s" % (header, body)
+
+		# try to receive in one go
+		transport.onreceive(self.expect_receive(body, msg='Failed: In one go'))
+		transport._on_receive(message)
+		self.assertTrue(self.have_received_expected(), msg='Failed: In one go')
+		
+		# try to receive in chunks
+		chunk1, chunk2, chunk3  = message[:20], message[20:73], message[73:]
+		nextmessage_chunk = "\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type: text/x"
+		chunks = (chunk1, chunk2, chunk3, nextmessage_chunk)
+	
+		transport.onreceive(self.expect_receive(body, msg='Failed: In chunks'))
+		for chunk in chunks:
+			transport._on_receive(chunk)
+		self.assertTrue(self.have_received_expected(), msg='Failed: In chunks')
+
+if __name__ == '__main__':
+	unittest.main()
+
+# vim: se ts=3:
-- 
GitLab