[stem/master] Using mocking module for protocolinfo unit tests

commit 9a2b50138356f317220819b2351f666bd45564e1 Author: Damian Johnson <atagar@torproject.org> Date: Fri Jan 27 18:53:39 2012 -0800 Using mocking module for protocolinfo unit tests Expanding the mocking module to provide a ControlMessage (very frequently needed in tests) and using the module to simplify the protocolinfo unit tests. --- test/mocking.py | 38 +++++++++++++++++++---- test/unit/connection/protocolinfo.py | 54 +++++++++++++++------------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/test/mocking.py b/test/mocking.py index f6bf98c..0d01a60 100644 --- a/test/mocking.py +++ b/test/mocking.py @@ -1,6 +1,11 @@ """ Helper functions for creating mock objects and monkey patching to help with -testing. +testing. With python's builtin unit testing framework the setUp and test +functions set up mocking, which is then reverted in the tearDown method by +calling 'revert_mocking'. + +mock - replaces a function with an alternative implementation +revert_mocking - reverts any changes made by the mock function Mocking Functions no_op - does nothing @@ -10,10 +15,9 @@ Mocking Functions return_none - returns None raise_exception - raises an exception when called -mock - replaces a function with an alternative implementation -revert_mocking - reverts any changes made by the mock function - -get_protocolinfo_response - provides a ProtocolInfoResponse instance +Instance Constructors + get_message - stem.socket.ControlMessage + get_protocolinfo_response - stem.connection.ProtocolInfoResponse """ import inspect @@ -94,6 +98,27 @@ def revert_mocking(): MOCK_STATE.clear() +def get_message(content, reformat = True): + """ + Provides a ControlMessage with content modified to be parsable. This makes + the following changes unless 'reformat' is false... + - ensures the content ends with a newline + - newlines are replaced with a carrage return and newline pair + + Arguments: + content (str) - base content for the controller message + reformat (str) - modifies content to be more accomidateing to being parsed + + Returns: + stem.socket.ControlMessage instance + """ + + if reformat: + if not content.endswith("\n"): content += "\n" + content = content.replace("\n", "\r\n") + + return stem.socket.recv_message(StringIO.StringIO(content)) + def get_protocolinfo_response(**attributes): """ Provides a ProtocolInfoResponse, customized with the given attributes. The @@ -107,8 +132,7 @@ def get_protocolinfo_response(**attributes): stem.connection.ProtocolInfoResponse instance """ - control_message = "250-PROTOCOLINFO 1\r\n250 OK\r\n" - protocolinfo_response = stem.socket.recv_message(StringIO.StringIO(control_message)) + protocolinfo_response = get_message("250-PROTOCOLINFO 1\n250 OK") stem.connection.ProtocolInfoResponse.convert(protocolinfo_response) for attr in attributes: diff --git a/test/unit/connection/protocolinfo.py b/test/unit/connection/protocolinfo.py index bc0fef3..0f47fe1 100644 --- a/test/unit/connection/protocolinfo.py +++ b/test/unit/connection/protocolinfo.py @@ -3,51 +3,44 @@ Unit tests for the stem.connection.ProtocolInfoResponse class. """ import unittest -import StringIO import stem.connection import stem.socket import stem.version +import test.mocking as mocking NO_AUTH = """250-PROTOCOLINFO 1 250-AUTH METHODS=NULL 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" PASSWORD_AUTH = """250-PROTOCOLINFO 1 250-AUTH METHODS=HASHEDPASSWORD 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" COOKIE_AUTH = r"""250-PROTOCOLINFO 1 250-AUTH METHODS=COOKIE COOKIEFILE="/tmp/my data\\\"dir//control_auth_cookie" 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" MULTIPLE_AUTH = """250-PROTOCOLINFO 1 250-AUTH METHODS=COOKIE,HASHEDPASSWORD COOKIEFILE="/home/atagar/.tor/control_auth_cookie" 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" UNKNOWN_AUTH = """250-PROTOCOLINFO 1 250-AUTH METHODS=MAGIC,HASHEDPASSWORD,PIXIE_DUST 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" MINIMUM_RESPONSE = """250-PROTOCOLINFO 5 -250 OK -""".replace("\n", "\r\n") +250 OK""" RELATIVE_COOKIE_PATH = r"""250-PROTOCOLINFO 1 250-AUTH METHODS=COOKIE COOKIEFILE="./tor-browser_en-US/Data/control_auth_cookie" 250-VERSION Tor="0.2.1.30" -250 OK -""".replace("\n", "\r\n") +250 OK""" class TestProtocolInfoResponse(unittest.TestCase): """ @@ -61,7 +54,7 @@ class TestProtocolInfoResponse(unittest.TestCase): """ # working case - control_message = stem.socket.recv_message(StringIO.StringIO(NO_AUTH)) + control_message = mocking.get_message(NO_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) # now this should be a ProtocolInfoResponse (ControlMessage subclass) @@ -69,14 +62,15 @@ class TestProtocolInfoResponse(unittest.TestCase): self.assertTrue(isinstance(control_message, stem.connection.ProtocolInfoResponse)) # exercise some of the ControlMessage functionality + raw_content = (NO_AUTH + "\n").replace("\n", "\r\n") + self.assertEquals(raw_content, control_message.raw_content()) self.assertTrue(str(control_message).startswith("PROTOCOLINFO 1")) - self.assertEquals(NO_AUTH, control_message.raw_content()) # attempt to convert the wrong type self.assertRaises(TypeError, stem.connection.ProtocolInfoResponse.convert, "hello world") # attempt to convert a different message type - bw_event_control_message = stem.socket.recv_message(StringIO.StringIO("650 BW 32326 2856\r\n")) + bw_event_control_message = mocking.get_message("650 BW 32326 2856") self.assertRaises(stem.socket.ProtocolError, stem.connection.ProtocolInfoResponse.convert, bw_event_control_message) def test_no_auth(self): @@ -84,7 +78,7 @@ class TestProtocolInfoResponse(unittest.TestCase): Checks a response when there's no authentication. """ - control_message = stem.socket.recv_message(StringIO.StringIO(NO_AUTH)) + control_message = mocking.get_message(NO_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals(1, control_message.protocol_version) @@ -98,7 +92,7 @@ class TestProtocolInfoResponse(unittest.TestCase): Checks a response with password authentication. """ - control_message = stem.socket.recv_message(StringIO.StringIO(PASSWORD_AUTH)) + control_message = mocking.get_message(PASSWORD_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals((stem.connection.AuthMethod.PASSWORD, ), control_message.auth_methods) @@ -108,7 +102,7 @@ class TestProtocolInfoResponse(unittest.TestCase): characters. """ - control_message = stem.socket.recv_message(StringIO.StringIO(COOKIE_AUTH)) + control_message = mocking.get_message(COOKIE_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals((stem.connection.AuthMethod.COOKIE, ), control_message.auth_methods) self.assertEquals("/tmp/my data\\\"dir//control_auth_cookie", control_message.cookie_path) @@ -118,7 +112,7 @@ class TestProtocolInfoResponse(unittest.TestCase): Checks a response with multiple authentication methods. """ - control_message = stem.socket.recv_message(StringIO.StringIO(MULTIPLE_AUTH)) + control_message = mocking.get_message(MULTIPLE_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals((stem.connection.AuthMethod.COOKIE, stem.connection.AuthMethod.PASSWORD), control_message.auth_methods) self.assertEquals("/home/atagar/.tor/control_auth_cookie", control_message.cookie_path) @@ -128,7 +122,7 @@ class TestProtocolInfoResponse(unittest.TestCase): Checks a response with an unrecognized authtentication method. """ - control_message = stem.socket.recv_message(StringIO.StringIO(UNKNOWN_AUTH)) + control_message = mocking.get_message(UNKNOWN_AUTH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals((stem.connection.AuthMethod.UNKNOWN, stem.connection.AuthMethod.PASSWORD), control_message.auth_methods) self.assertEquals(("MAGIC", "PIXIE_DUST"), control_message.unknown_auth_methods) @@ -139,7 +133,7 @@ class TestProtocolInfoResponse(unittest.TestCase): information to be a valid response. """ - control_message = stem.socket.recv_message(StringIO.StringIO(MINIMUM_RESPONSE)) + control_message = mocking.get_message(MINIMUM_RESPONSE) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals(5, control_message.protocol_version) @@ -162,23 +156,23 @@ class TestProtocolInfoResponse(unittest.TestCase): def call_mocking(command): if command == stem.util.system.GET_PID_BY_NAME_PGREP % "tor": return ["10"] - if command == stem.util.system.GET_CWD_PWDX % 10: + elif command == stem.util.system.GET_CWD_PWDX % 10: return ["10: /tmp/foo"] - stem.util.system.CALL_MOCKING = call_mocking + mocking.mock(stem.util.system.call, call_mocking) - control_message = stem.socket.recv_message(StringIO.StringIO(RELATIVE_COOKIE_PATH)) + control_message = mocking.get_message(RELATIVE_COOKIE_PATH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals("/tmp/foo/tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_path) # exercise cookie expansion where both calls fail (should work, just # leaving the path unexpanded) - stem.util.system.CALL_MOCKING = lambda cmd: None - control_message = stem.socket.recv_message(StringIO.StringIO(RELATIVE_COOKIE_PATH)) + mocking.mock(stem.util.system.call, mocking.return_none()) + control_message = mocking.get_message(RELATIVE_COOKIE_PATH) stem.connection.ProtocolInfoResponse.convert(control_message) self.assertEquals("./tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_path) # reset system call mocking - stem.util.system.CALL_MOCKING = None + mocking.revert_mocking()
participants (1)
-
atagar@torproject.org