[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