[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Unit testing for stem.types.ControlLine
commit 40b7ab1b36dd4bd0ce455714ea8a9dfd4d3bb76b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat Nov 5 17:18:10 2011 -0700
Unit testing for stem.types.ControlLine
General unit tests to exercise the ControlLine class with PROTOCOLINFO output.
This also has a minor fix so we throw an IndexError rather than ValueError when
pop_mapping() is called while empty.
---
stem/types.py | 8 ++
test/unit/types/control_line.py | 135 +++++++++++++++++++++++++++++++++++++++
2 files changed, 143 insertions(+), 0 deletions(-)
diff --git a/stem/types.py b/stem/types.py
index 1dc03e2..d152542 100644
--- a/stem/types.py
+++ b/stem/types.py
@@ -34,6 +34,13 @@ from stem.util import log
KEY_ARG = re.compile("^(\S+)=")
# Escape sequences from the 'esc_for_log' function of tor's 'common/util.c'.
+# It's hard to tell what controller functions use this in practice, but direct
+# users are...
+# - 'COOKIEFILE' field of PROTOCOLINFO responses
+# - logged messages about bugs
+# - the 'getinfo_helper_listeners' function of control.c which looks to be dead
+# code
+
CONTROL_ESCAPES = {r"\\": "\\", r"\"": "\"", r"\'": "'",
r"\r": "\r", r"\n": "\n", r"\t": "\t"}
@@ -350,6 +357,7 @@ class ControlLine(str):
try:
self._remainder_lock.acquire()
+ if self.is_empty(): raise IndexError("no remaining content to parse")
key_match = KEY_ARG.match(self._remainder)
if not key_match:
diff --git a/test/unit/types/control_line.py b/test/unit/types/control_line.py
index d6bda8d..7f7349c 100644
--- a/test/unit/types/control_line.py
+++ b/test/unit/types/control_line.py
@@ -5,6 +5,14 @@ Unit tests for the types.ControlLine class.
import unittest
import stem.types
+# response made by having 'DataDirectory /tmp/my data\"dir/' in the torrc
+PROTOCOLINFO_RESPONSE = (
+ 'PROTOCOLINFO 1',
+ 'AUTH METHODS=COOKIE COOKIEFILE="/tmp/my data\\\\\\"dir//control_auth_cookie"',
+ 'VERSION Tor="0.2.1.30"',
+ 'OK',
+)
+
class TestControlLine(unittest.TestCase):
"""
Tests methods of the types.ControlLine class.
@@ -22,4 +30,131 @@ class TestControlLine(unittest.TestCase):
line = stem.types.ControlLine("\"this has a \\\" and \\\\ in it\" foo=bar more_data")
self.assertEquals(line.pop(True, True), "this has a \" and \\ in it")
+
+ def test_string(self):
+ """
+ Basic checks that we behave as a regular immutable string.
+ """
+
+ line = stem.types.ControlLine(PROTOCOLINFO_RESPONSE[0])
+ self.assertEquals(line, 'PROTOCOLINFO 1')
+ self.assertTrue(line.startswith('PROTOCOLINFO '))
+
+ # checks that popping items doesn't effect us
+ line.pop()
+ self.assertEquals(line, 'PROTOCOLINFO 1')
+ self.assertTrue(line.startswith('PROTOCOLINFO '))
+
+ def test_general_usage(self):
+ """
+ Checks a basic use case for the popping entries.
+ """
+
+ # pops a series of basic, space separated entries
+ line = stem.types.ControlLine(PROTOCOLINFO_RESPONSE[0])
+ self.assertEquals(line.remainder(), 'PROTOCOLINFO 1')
+ self.assertFalse(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+
+ self.assertRaises(ValueError, line.pop_mapping)
+ self.assertEquals(line.pop(), 'PROTOCOLINFO')
+ self.assertEquals(line.remainder(), '1')
+ self.assertFalse(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+
+ self.assertRaises(ValueError, line.pop_mapping)
+ self.assertEquals(line.pop(), '1')
+ self.assertEquals(line.remainder(), '')
+ self.assertTrue(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+
+ self.assertRaises(IndexError, line.pop_mapping)
+ self.assertRaises(IndexError, line.pop)
+ self.assertEquals(line.remainder(), '')
+ self.assertTrue(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+
+ def test_pop_mapping(self):
+ """
+ Checks use cases when parsing KEY=VALUE mappings.
+ """
+
+ # version entry with a space
+ version_entry = 'Tor="0.2.1.30 (0a083b0188cacd2f07838ff0446113bd5211a024)"'
+
+ line = stem.types.ControlLine(version_entry)
+ self.assertEquals(line.remainder(), version_entry)
+ self.assertFalse(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertTrue(line.is_next_mapping())
+ self.assertTrue(line.is_next_mapping(True))
+
+ # try popping this as a non-quoted mapping
+ self.assertEquals(line.pop_mapping(), ('Tor', '"0.2.1.30'))
+ self.assertEquals(line.remainder(), '(0a083b0188cacd2f07838ff0446113bd5211a024)"')
+ self.assertFalse(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+ self.assertRaises(ValueError, line.pop_mapping)
+
+ # try popping this as a quoted mapping
+ line = stem.types.ControlLine(version_entry)
+ self.assertEquals(line.pop_mapping(True), ('Tor', '0.2.1.30 (0a083b0188cacd2f07838ff0446113bd5211a024)'))
+ self.assertEquals(line.remainder(), '')
+ self.assertTrue(line.is_empty())
+ self.assertFalse(line.is_next_quoted())
+ self.assertFalse(line.is_next_mapping())
+
+ def test_escapes(self):
+ """
+ Checks that we can parse quoted values with escaped quotes in it. This
+ explicitely comes up with the COOKIEFILE attribute of PROTOCOLINFO
+ responses.
+ """
+
+ auth_line = PROTOCOLINFO_RESPONSE[1]
+ line = stem.types.ControlLine(auth_line)
+ self.assertEquals(line, auth_line)
+ self.assertEquals(line.remainder(), auth_line)
+
+ self.assertEquals(line.pop(), "AUTH")
+ self.assertEquals(line.pop_mapping(), ("METHODS", "COOKIE"))
+
+ self.assertEquals(line.remainder(), r'COOKIEFILE="/tmp/my data\\\"dir//control_auth_cookie"')
+ self.assertTrue(line.is_next_mapping())
+ self.assertTrue(line.is_next_mapping(True))
+ self.assertTrue(line.is_next_mapping(True, True))
+ cookie_file_entry = line.remainder()
+
+ # try a general pop
+ self.assertEquals(line.pop(), 'COOKIEFILE="/tmp/my')
+ self.assertEquals(line.pop(), r'data\\\"dir//control_auth_cookie"')
+ self.assertTrue(line.is_empty())
+
+ # try a general pop with escapes
+ line = stem.types.ControlLine(cookie_file_entry)
+ self.assertEquals(line.pop(escaped = True), 'COOKIEFILE="/tmp/my')
+ self.assertEquals(line.pop(escaped = True), r'data\"dir//control_auth_cookie"')
+ self.assertTrue(line.is_empty())
+
+ # try a mapping pop
+ line = stem.types.ControlLine(cookie_file_entry)
+ self.assertEquals(line.pop_mapping(), ('COOKIEFILE', '"/tmp/my'))
+ self.assertEquals(line.remainder(), r'data\\\"dir//control_auth_cookie"')
+ self.assertFalse(line.is_empty())
+
+ # try a quoted mapping pop (this should trip up on the escaped quote)
+ line = stem.types.ControlLine(cookie_file_entry)
+ self.assertEquals(line.pop_mapping(True), ('COOKIEFILE', '/tmp/my data\\\\\\'))
+ self.assertEquals(line.remainder(), 'dir//control_auth_cookie"')
+ self.assertFalse(line.is_empty())
+
+ # try an escaped quoted mapping pop
+ line = stem.types.ControlLine(cookie_file_entry)
+ self.assertEquals(line.pop_mapping(True, True), ('COOKIEFILE', r'/tmp/my data\"dir//control_auth_cookie'))
+ self.assertTrue(line.is_empty())
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits