[stem/master] Unit tests for PROTOCOLINFO responses

commit 2b797d0ba9310a015bef5bc2605020ebe8ffafdd Author: Damian Johnson <atagar@torproject.org> Date: Sun Nov 13 13:15:49 2011 -0800 Unit tests for PROTOCOLINFO responses --- run_tests.py | 2 + stem/connection.py | 10 +- test/unit/connection/__init__.py | 6 + test/unit/connection/protocolinfo_response.py | 144 +++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) diff --git a/run_tests.py b/run_tests.py index 0b86706..1107a59 100755 --- a/run_tests.py +++ b/run_tests.py @@ -15,6 +15,7 @@ import test.runner import test.unit.types.control_message import test.unit.types.control_line import test.unit.types.version +import test.unit.connection.protocolinfo_response import test.integ.message import test.integ.system @@ -29,6 +30,7 @@ DIVIDER = "=" * 70 UNIT_TESTS = (("stem.types.ControlMessage", test.unit.types.control_message.TestControlMessage), ("stem.types.ControlLine", test.unit.types.control_line.TestControlLine), ("stem.types.Version", test.unit.types.version.TestVerion), + ("stem.connection.ProtocolInfoResponse", test.unit.connection.protocolinfo_response.TestProtocolInfoResponse), ) INTEG_TESTS = (("stem.types.ControlMessage", test.integ.message.TestMessageFunctions), diff --git a/stem/connection.py b/stem/connection.py index d17efde..2b96899 100644 --- a/stem/connection.py +++ b/stem/connection.py @@ -53,7 +53,8 @@ class ProtocolInfoResponse(stem.types.ControlMessage): def convert(control_message): """ - Parses a ControlMessage, converting it into a ProtocolInfoResponse. + Parses a ControlMessage, performing an in-place conversion of it into a + ProtocolInfoResponse. Arguments: control_message (stem.types.ControlMessage) - @@ -61,7 +62,7 @@ class ProtocolInfoResponse(stem.types.ControlMessage): Raises: stem.types.ProtocolError the message isn't a proper PROTOCOLINFO response - ValueError if argument is of the wrong type + TypeError if argument isn't a ControlMessage """ if isinstance(control_message, stem.types.ControlMessage): @@ -69,7 +70,7 @@ class ProtocolInfoResponse(stem.types.ControlMessage): control_message._parse_message() return control_message else: - raise ValueError("Only able to convert stem.types.ControlMessage instances") + raise TypeError("Only able to convert stem.types.ControlMessage instances") convert = staticmethod(convert) @@ -152,7 +153,7 @@ class ProtocolInfoResponse(stem.types.ControlMessage): # parse optional COOKIEFILE mapping (quoted and can have escapes) if line.is_next_mapping("COOKIEFILE", True, True): - self.cookie_file = line.pop_mapping(True, True)[0] + self.cookie_file = line.pop_mapping(True, True)[1] # attempt to expand relative cookie paths if stem.util.system.is_relative_path(self.cookie_file): @@ -286,6 +287,7 @@ class ControlConnection: while self.is_running(): try: + # TODO: this raises a SocketClosed when... well, the socket is closed control_message = stem.types.read_message(self._control_socket_file) if control_message.content()[-1][0] == "650": diff --git a/test/unit/connection/__init__.py b/test/unit/connection/__init__.py new file mode 100644 index 0000000..440773e --- /dev/null +++ b/test/unit/connection/__init__.py @@ -0,0 +1,6 @@ +""" +Unit tests for stem.connection. +""" + +__all__ = ["protocolinfo_response"] + diff --git a/test/unit/connection/protocolinfo_response.py b/test/unit/connection/protocolinfo_response.py new file mode 100644 index 0000000..0dcde95 --- /dev/null +++ b/test/unit/connection/protocolinfo_response.py @@ -0,0 +1,144 @@ +""" +Unit tests for the stem.connection.ProtocolInfoResponse class. +""" + +import unittest +import StringIO +import stem.connection +import stem.types + +NO_AUTH = """250-PROTOCOLINFO 1 +250-AUTH METHODS=NULL +250-VERSION Tor="0.2.1.30" +250 OK +""".replace("\n", "\r\n") + +PASSWORD_AUTH = """250-PROTOCOLINFO 1 +250-AUTH METHODS=HASHEDPASSWORD +250-VERSION Tor="0.2.1.30" +250 OK +""".replace("\n", "\r\n") + +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") + +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") + +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") + +MINIMUM_RESPONSE = """250-PROTOCOLINFO 5 +250 OK +""".replace("\n", "\r\n") + +class TestProtocolInfoResponse(unittest.TestCase): + """ + Tests the parsing of ControlMessages for PROTOCOLINFO responses. + """ + + def test_convert(self): + """ + Exercises functionality of the convert method both when it works and + there's an error. + """ + + # working case + control_message = stem.types.read_message(StringIO.StringIO(NO_AUTH)) + stem.connection.ProtocolInfoResponse.convert(control_message) + + # now this should be a ProtocolInfoResponse (ControlMessage subclass) + self.assertTrue(isinstance(control_message, stem.types.ControlMessage)) + self.assertTrue(isinstance(control_message, stem.connection.ProtocolInfoResponse)) + + # exercise some of the ControlMessage functionality + 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.types.read_message(StringIO.StringIO("650 BW 32326 2856\r\n")) + self.assertRaises(stem.types.ProtocolError, stem.connection.ProtocolInfoResponse.convert, bw_event_control_message) + + def test_no_auth(self): + """ + Checks a response when there's no authentication. + """ + + control_message = stem.types.read_message(StringIO.StringIO(NO_AUTH)) + stem.connection.ProtocolInfoResponse.convert(control_message) + + self.assertEquals(1, control_message.protocol_version) + self.assertEquals(stem.types.Version("0.2.1.30"), control_message.tor_version) + self.assertEquals((stem.connection.AuthMethod.NONE, ), control_message.auth_methods) + self.assertEquals((), control_message.unknown_auth_methods) + self.assertEquals(None, control_message.cookie_file) + self.assertEquals(None, control_message.socket) + + def test_password_auth(self): + """ + Checks a response with password authentication. + """ + + control_message = stem.types.read_message(StringIO.StringIO(PASSWORD_AUTH)) + stem.connection.ProtocolInfoResponse.convert(control_message) + self.assertEquals((stem.connection.AuthMethod.PASSWORD, ), control_message.auth_methods) + + def test_cookie_auth(self): + """ + Checks a response with cookie authentication and a path including escape + characters. + """ + + control_message = stem.types.read_message(StringIO.StringIO(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_file) + + def test_multiple_auth(self): + """ + Checks a response with multiple authentication methods. + """ + + control_message = stem.types.read_message(StringIO.StringIO(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_file) + + def test_unknown_auth(self): + """ + Checks a response with an unrecognized authtentication method. + """ + + control_message = stem.types.read_message(StringIO.StringIO(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) + + def test_minimum_response(self): + """ + Checks a PROTOCOLINFO response that only contains the minimum amount of + information to be a valid response. + """ + + control_message = stem.types.read_message(StringIO.StringIO(MINIMUM_RESPONSE)) + stem.connection.ProtocolInfoResponse.convert(control_message) + + self.assertEquals(5, control_message.protocol_version) + self.assertEquals(None , control_message.tor_version) + self.assertEquals((), control_message.auth_methods) + self.assertEquals((), control_message.unknown_auth_methods) + self.assertEquals(None, control_message.cookie_file) + self.assertEquals(None, control_message.socket) +
participants (1)
-
atagar@torproject.org