[tor-commits] [stem/master] Basic unit tests for ControlMessage

atagar at torproject.org atagar at torproject.org
Tue Oct 11 04:26:10 UTC 2011


commit 5b1a7e8f2d375db5ad4d3c212fbae93ed4724277
Author: Damian Johnson <atagar at torproject.org>
Date:   Mon Oct 10 21:24:58 2011 -0700

    Basic unit tests for ControlMessage
    
    Just some sanity checks that ControlMessage parses common GETINFO responses.
    I'll expand on these including some error cases next.
---
 run_tests.py          |   32 ++++++++++++++---
 stem/connection.py    |    9 +++--
 stem/types.py         |    9 +++--
 test/unit/__init__.py |    2 +-
 test/unit/message.py  |   87 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/unit/version.py  |    2 +-
 6 files changed, 125 insertions(+), 16 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index e1c0811..78f392d 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -7,13 +7,19 @@ Runs unit and integration tests.
 import sys
 import getopt
 import unittest
+import test.unit.message
 import test.unit.version
 
 from stem.util import enum, term
 
 OPT = "uit:h"
 OPT_EXPANDED = ["unit", "integ", "targets=", "help"]
-DIVIDER = "=" * 80
+DIVIDER = "=" * 70
+
+# (name, class) tuples for all of our unit tests
+UNIT_TESTS = (("stem.types.ControlMessage", test.unit.message.TestMessageFunctions),
+              ("stem.types.Version", test.unit.version.TestVerionFunctions),
+             )
 
 # Configurations that the intergration tests can be ran with. Attributs are
 # tuples of the test runner and description.
@@ -87,15 +93,29 @@ if __name__ == '__main__':
     sys.exit()
   
   if run_unit_tests:
-    print "%s\nUnit Tests\n%s\n" % (DIVIDER, DIVIDER)
+    print "%s\n%s\n%s\n" % (DIVIDER, "UNIT TESTS".center(70), DIVIDER)
+    
+    for name, test_class in UNIT_TESTS:
+      print "%s\n%s\n%s\n" % (DIVIDER, name, DIVIDER)
+      #print name
+      suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
+      unittest.TextTestRunner(verbosity=2).run(suite)
+      print
+    
+    #import test.unit
+    #suite = unittest.TestLoader().loadTestsFromTestCase(test.unit.version.TestVerionFunctions)
+    #suite = unittest.TestLoader().discover("test/unit/", "*.py")
+    #suite.addTests(unittest.loader.loadTestsFromTestCase(test.unit.message.TestMessageFunctions))
     
-    suite = unittest.TestLoader().loadTestsFromTestCase(test.unit.version.TestVerionFunctions)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    #suite = unittest.TestLoader()
+    #suite.loadTestsFromTestCase(test.unit.message.TestMessageFunctions)
+    #suite.loadTestsFromTestCase(test.unit.version.TestVerionFunctions)
+    #unittest.TextTestRunner(verbosity=2).run(suite)
     
-    print ""
+    print
   
   if run_integ_tests:
-    print "%s\nIntegration Tests\n%s\n" % (DIVIDER, DIVIDER)
+    print "%s\n%s\n%s\n" % (DIVIDER, "INTEGRATION TESTS".center(70), DIVIDER)
     
     for target in integ_targets:
       runner, description = TARGET_ATTR[target]
diff --git a/stem/connection.py b/stem/connection.py
index 5e1fe5e..4853864 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -14,7 +14,7 @@ class ControlConnection:
   """
   Connection to a Tor control port. This is a very lightweight wrapper around
   the socket, providing basic process communication and event listening. Don't
-  use this directly - subclasses provide frendlier controller access.
+  use this directly - subclasses provide friendlier controller access.
   """
   
   def __init__(self, control_socket):
@@ -59,7 +59,8 @@ class ControlConnection:
     whenever we receive an event from the control socket.
     
     Arguments:
-      event_message (ControlMessage) - message received from the control socket
+      event_message (types.ControlMessage) - message received from the control
+                                             socket
     """
     
     pass
@@ -72,7 +73,7 @@ class ControlConnection:
       message (str) - message to be sent to the control socket
     
     Returns:
-      ControlMessage with the response from the control socket
+      types.ControlMessage with the response from the control socket
     """
     
     # makes sure that the message ends with a CRLF
@@ -88,7 +89,7 @@ class ControlConnection:
   def _event_loop(self):
     """
     Continually pulls messages from the _event_thread and sends them to
-    handle_event. This is done via its own thread so subclasses with a lenghty
+    handle_event. This is done via its own thread so subclasses with a lengthy
     handle_event implementation don't block further reading from the socket.
     """
     
diff --git a/stem/types.py b/stem/types.py
index c73a769..d37f9a1 100644
--- a/stem/types.py
+++ b/stem/types.py
@@ -1,5 +1,6 @@
 """
-Classes for miscellaneous tor object. This includes...
+Class representations for a variety of tor objects. These are most commonly
+return values rather than being instantiated by users directly.
 
 ProtocolError - Malformed socket data.
 ControlSocketClosed - Socket terminated.
@@ -92,12 +93,12 @@ def read_message(control_file):
         
         line = line[:-2] # strips off the CRLF
         
-        # lines starting with a pariod are escaped by a second period (as per
+        # lines starting with a period are escaped by a second period (as per
         # section 2.4 of the control-spec)
         if line.startswith(".."): line = line[1:]
         
         # appends to previous content, using a newline rather than CRLF
-        # separator (more contentional for multi-line string content outside
+        # separator (more conventional for multi-line string content outside
         # the windows world)
         
         content += "\n" + line
@@ -247,7 +248,7 @@ class Version:
   
   def __cmp__(self, other):
     """
-    Simple comparision of versions. An undefined patch level is treated as zero
+    Simple comparison of versions. An undefined patch level is treated as zero
     and status tags are compared lexically (as per the version spec).
     """
     
diff --git a/test/unit/__init__.py b/test/unit/__init__.py
index d7db1bd..52a6924 100644
--- a/test/unit/__init__.py
+++ b/test/unit/__init__.py
@@ -2,5 +2,5 @@
 Unit tests for the stem library.
 """
 
-__all__ = ["version"]
+__all__ = ["message", "version"]
 
diff --git a/test/unit/message.py b/test/unit/message.py
new file mode 100644
index 0000000..dde2c9c
--- /dev/null
+++ b/test/unit/message.py
@@ -0,0 +1,87 @@
+"""
+Unit tests for the types.ControlMessage parsing and class.
+"""
+
+import StringIO
+import unittest
+import stem.types
+
+GETINFO_VERSION_REPLY = """250-version=0.2.2.23-alpha (git-b85eb949b528f4d7)\r
+250 OK\r
+"""
+
+GETINFO_INFONAMES_REPLY = """250+info/names=\r
+accounting/bytes -- Number of bytes read/written so far in the accounting interval.\r
+accounting/bytes-left -- Number of bytes left to write/read so far in the accounting interval.\r
+accounting/enabled -- Is accounting currently enabled?\r
+accounting/hibernating -- Are we hibernating or awake?\r
+stream-status -- List of current streams.\r
+version -- The current version of Tor.\r
+.\r
+250 OK\r
+"""
+
+class TestMessageFunctions(unittest.TestCase):
+  """
+  Tests methods and functions related to 'types.ControlMessage'. This uses
+  StringIO to make 'files' to mock socket input.
+  """
+  
+  def test_getinfo_results(self):
+    """
+    Checks parsing against some actual GETINFO responses.
+    """
+    
+    # GETINFO version (basic single-line results)
+    message = self.assert_message_parses(GETINFO_VERSION_REPLY)
+    self.assertEquals(2, len(list(message)))
+    self.assertEquals(2, len(str(message).split("\n")))
+    
+    # manually checks the contents
+    contents = message.content()
+    self.assertEquals(2, len(contents))
+    self.assertEquals(("250", "-", "version=0.2.2.23-alpha (git-b85eb949b528f4d7)"), contents[0])
+    self.assertEquals(("250", " ", "OK"), contents[1])
+    
+    # GETINFO info/names (data entry)
+    message = self.assert_message_parses(GETINFO_INFONAMES_REPLY)
+    self.assertEquals(2, len(list(message)))
+    self.assertEquals(8, len(str(message).split("\n")))
+    
+    # manually checks the contents
+    contents = message.content()
+    self.assertEquals(2, len(contents))
+    
+    first_entry = (contents[0][0], contents[0][1], contents[0][2][:contents[0][2].find("\n")])
+    self.assertEquals(("250", "+", "info/names="), first_entry)
+    self.assertEquals(("250", " ", "OK"), contents[1])
+  
+  def assert_message_parses(self, controller_reply):
+    """
+    Performs some basic sanity checks that a reply mirrors its parsed result.
+    
+    Returns:
+      types.ControlMessage for the given input
+    """
+    
+    message = stem.types.read_message(StringIO.StringIO(controller_reply))
+    
+    # checks that the raw_content equals the input value
+    self.assertEqual(controller_reply, message.raw_content())
+    
+    # checks that the contents match the input
+    message_lines = str(message).split("\n")
+    controller_lines = controller_reply.split("\r\n")
+    controller_lines.pop() # the ControlMessage won't have a trailing newline
+    
+    while controller_lines:
+      line = controller_lines.pop(0)
+      
+      # mismatching lines with just a period are probably data termination
+      if line == "." and (not message_lines or line != message_lines[0]):
+        continue
+      
+      self.assertTrue(line.endswith(message_lines.pop(0)))
+    
+    return message
+
diff --git a/test/unit/version.py b/test/unit/version.py
index f241a8c..0c4a34a 100644
--- a/test/unit/version.py
+++ b/test/unit/version.py
@@ -1,5 +1,5 @@
 """
-Unit tests for types functions and classes.
+Unit tests for the types.Version parsing and class.
 """
 
 import unittest



More information about the tor-commits mailing list