self.serv = None
class ThreadableTest:
+ """Threadable Test class
+
+ The ThreadableTest class makes it easy to create a threaded
+ client/server pair from an existing unit test. To create a
+ new threaded class from an existing unit test, use multiple
+ inheritance:
+
+ class NewClass (OldClass, ThreadableTest):
+ pass
+
+ This class defines two new fixture functions with obvious
+ purposes for overriding:
+
+ clientSetUp ()
+ clientTearDown ()
+
+ Any new test functions within the class must then define
+ tests in pairs, where the test name is preceeded with a
+ '_' to indicate the client portion of the test. Ex:
+
+ def testFoo(self):
+ # Server portion
+
+ def _testFoo(self):
+ # Client portion
+
+ Any exceptions raised by the clients during their tests
+ are caught and transferred to the main thread to alert
+ the testing framework.
+
+ Note, the server setup function cannot call any blocking
+ functions that rely on the client thread during setup,
+ unless serverExplicityReady() is called just before
+ the blocking call (such as in setting up a client/server
+ connection and performing the accept() in setUp().
+ """
def __init__(self):
# Swap the true setup function
self.setUp = self._setUp
self.tearDown = self._tearDown
+ def serverExplicitReady(self):
+ """This method allows the server to explicitly indicate that
+ it wants the client thread to proceed. This is useful if the
+ server is about to execute a blocking routine that is
+ dependent upon the client thread during its setup routine."""
+ self.server_ready.set()
+
def _setUp(self):
- self.ready = threading.Event()
+ self.server_ready = threading.Event()
+ self.client_ready = threading.Event()
self.done = threading.Event()
self.queue = Queue.Queue(1)
self.clientRun, (test_method,))
self.__setUp()
- self.ready.wait()
+ if not self.server_ready.isSet():
+ self.server_ready.set()
+ self.client_ready.wait()
def _tearDown(self):
self.__tearDown()
self.fail(msg)
def clientRun(self, test_func):
- self.ready.set()
+ self.server_ready.wait()
+ self.client_ready.set()
self.clientSetUp()
if not callable(test_func):
raise TypeError, "test_func must be a callable function"
def setUp(self):
ThreadedTCPSocketTest.setUp(self)
+ # Indicate explicitly we're ready for the client thread to
+ # proceed and then perform the blocking call to accept
+ self.serverExplicitReady()
conn, addr = self.serv.accept()
self.cli_conn = conn
self.assertEqual(msg, MSG)
def _testRecvFrom(self):
- time.sleep(1) # Give server a chance to set up
self.cli.sendto(MSG, 0, (HOST, PORT))
class NonBlockingTCPTests(ThreadedTCPSocketTest):
self.fail("Error trying to do accept after select.")
def _testAccept(self):
- time.sleep(1)
self.cli.connect((HOST, PORT))
def testConnect(self):
"""Testing non-blocking connect."""
- time.sleep(1)
conn, addr = self.serv.accept()
def _testConnect(self):
def _testRecv(self):
self.cli.connect((HOST, PORT))
- time.sleep(1)
self.cli.send(MSG)
class FileObjectClassTestCase(SocketConnectedTest):
self.cli_file.write(MSG)
self.cli_file.flush()
-def main():
+def test_main():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(GeneralModuleTests))
suite.addTest(unittest.makeSuite(BasicTCPTest))
test_support.run_suite(suite)
if __name__ == "__main__":
- main()
+ test_main()