[tor-commits] [stem/master] Unit tests for PROTOCOLINFO responses

atagar at torproject.org atagar at torproject.org
Sun Nov 13 23:42:29 UTC 2011


commit 2b797d0ba9310a015bef5bc2605020ebe8ffafdd
Author: Damian Johnson <atagar at 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)
+





More information about the tor-commits mailing list