[tor-commits] [stem/master] Add normalize argument to ControlMessage.from_str()

atagar at torproject.org atagar at torproject.org
Mon May 22 18:30:29 UTC 2017


commit 144692cb801bd421b107db142e6e1e034961518e
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun May 21 18:16:47 2017 -0700

    Add normalize argument to ControlMessage.from_str()
    
    Our get_message() testing helper just did a little normalization. Adding this
    to ControlMessage.from_str() so callers don't need to do the whole carriage
    return stuff.
---
 stem/response/__init__.py           | 13 ++++++++++++-
 test/unit/doctest.py                |  6 +++---
 test/unit/interpreter/commands.py   | 17 +++++-----------
 test/unit/response/add_onion.py     | 21 +++++++-------------
 test/unit/response/authchallenge.py |  7 +++----
 test/unit/response/events.py        |  7 ++-----
 test/unit/response/getconf.py       | 25 ++++++++----------------
 test/unit/response/getinfo.py       | 27 +++++++++----------------
 test/unit/response/mapaddress.py    | 23 ++++++++--------------
 test/unit/response/protocolinfo.py  | 39 ++++++++++++-------------------------
 test/unit/response/singleline.py    | 23 +++++++---------------
 test/unit/tutorial_examples.py      |  9 +++------
 test/util.py                        | 27 +------------------------
 13 files changed, 80 insertions(+), 164 deletions(-)

diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index a84654c..eadb25c 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -132,19 +132,30 @@ class ControlMessage(object):
   """
 
   @staticmethod
-  def from_str(content, msg_type = None, **kwargs):
+  def from_str(content, msg_type = None, normalize = False, **kwargs):
     """
     Provides a ControlMessage for the given content.
 
     .. versionadded:: 1.1.0
 
+    .. versionchanged:: 1.6.0
+       Added the normalize argument.
+
     :param str content: message to construct the message from
     :param str msg_type: type of tor reply to parse the content as
+    :param bool normalize: ensures expected carriage return and ending newline
+      are present
     :param kwargs: optional keyword arguments to be passed to the parser method
 
     :returns: stem.response.ControlMessage instance
     """
 
+    if normalize:
+      if not content.endswith('\n'):
+        content += '\n'
+
+      content = re.sub('([\r]?)\n', '\r\n', content)
+
     msg = stem.socket.recv_message(io.StringIO(stem.util.str_tools._to_unicode(content)))
 
     if msg_type is not None:
diff --git a/test/unit/doctest.py b/test/unit/doctest.py
index 06a6eaa..1240324 100644
--- a/test/unit/doctest.py
+++ b/test/unit/doctest.py
@@ -13,9 +13,10 @@ import stem.util.connection
 import stem.util.str_tools
 import stem.util.system
 import stem.version
-
 import test.util
 
+from stem.response import ControlMessage
+
 try:
   # added in python 3.3
   from unittest.mock import Mock, patch
@@ -83,8 +84,7 @@ class TestDocumentation(unittest.TestCase):
           'circuit-status': EXPECTED_CIRCUIT_STATUS,
         }[arg]
 
-        response = test.util.get_message(ADD_ONION_RESPONSE)
-        stem.response.convert('ADD_ONION', response)
+        response = ControlMessage.from_str(ADD_ONION_RESPONSE, 'ADD_ONION', normalize = True)
         controller.create_ephemeral_hidden_service.return_value = response
 
         args['globs'] = {'controller': controller}
diff --git a/test/unit/interpreter/commands.py b/test/unit/interpreter/commands.py
index c8cbc09..020b14b 100644
--- a/test/unit/interpreter/commands.py
+++ b/test/unit/interpreter/commands.py
@@ -5,10 +5,8 @@ import stem
 import stem.response
 import stem.version
 
-import test.util
-
 from stem.interpreter.commands import ControlInterpreter, _get_fingerprint
-
+from stem.response import ControlMessage
 from test.unit.interpreter import CONTROLLER
 
 try:
@@ -126,8 +124,7 @@ class TestInterpreterCommands(unittest.TestCase):
     )
 
     for content in event_contents:
-      event = test.util.get_message(content)
-      stem.response.convert('EVENT', event)
+      event = ControlMessage.from_str(content, 'EVENT', normalize = True)
       interpreter._received_events.append(event)
 
     self.assertEqual(EXPECTED_EVENTS_RESPONSE, interpreter.run_command('/events'))
@@ -168,10 +165,8 @@ class TestInterpreterCommands(unittest.TestCase):
     self.assertEqual(expected, interpreter.run_command('/unrecognized'))
 
   def test_getinfo(self):
-    response = '250-version=0.2.5.1-alpha-dev (git-245ecfff36c0cecc)\r\n250 OK'
-
     controller = Mock()
-    controller.msg.return_value = test.util.get_message(response)
+    controller.msg.return_value = ControlMessage.from_str('250-version=0.2.5.1-alpha-dev (git-245ecfff36c0cecc)\r\n250 OK\r\n')
 
     interpreter = ControlInterpreter(controller)
 
@@ -183,10 +178,8 @@ class TestInterpreterCommands(unittest.TestCase):
     self.assertEqual('\x1b[1;31mkaboom!\x1b[0m\n', interpreter.run_command('getinfo process/user'))
 
   def test_getconf(self):
-    response = '250-Log=notice stdout\r\n250 Address'
-
     controller = Mock()
-    controller.msg.return_value = test.util.get_message(response)
+    controller.msg.return_value = ControlMessage.from_str('250-Log=notice stdout\r\n250 Address\r\n')
 
     interpreter = ControlInterpreter(controller)
 
@@ -195,7 +188,7 @@ class TestInterpreterCommands(unittest.TestCase):
 
   def test_setevents(self):
     controller = Mock()
-    controller.msg.return_value = test.util.get_message('250 OK')
+    controller.msg.return_value = ControlMessage.from_str('250 OK\r\n')
 
     interpreter = ControlInterpreter(controller)
 
diff --git a/test/unit/response/add_onion.py b/test/unit/response/add_onion.py
index 048ca67..9430250 100644
--- a/test/unit/response/add_onion.py
+++ b/test/unit/response/add_onion.py
@@ -8,7 +8,7 @@ import stem
 import stem.response
 import stem.response.add_onion
 
-import test.util
+from stem.response import ControlMessage
 
 WITH_PRIVATE_KEY = """250-ServiceID=gfzprpioee3hoppz
 250-PrivateKey=RSA1024:MIICXgIBAAKBgQDZvYVxvKPTWhId/8Ss9fVxjAoFDsrJ3pk6HjHrEFRm3ypkK/vArbG9BrupzzYcyms+lO06O8b/iOSHuZI5mUEGkrYqQ+hpB2SkPUEzW7vcp8SQQivna3+LfkWH4JDqfiwZutU6MMEvU6g1OqK4Hll6uHbLpsfxkS/mGjyu1C9a9wIDAQABAoGBAJxsC3a25xZJqaRFfxwmIiptSTFy+/nj4T4gPQo6k/fHMKP/+P7liT9bm+uUwbITNNIjmPzxvrcKt+pNRR/92fizxr8QXr8l0ciVOLerbvdqvVUaQ/K1IVsblOLbactMvXcHactmqqLFUaZU9PPSDla7YkzikLDIUtHXQBEt4HEhAkEA/c4n+kpwi4odCaF49ESPbZC/Qejh7U9Tq10vAHzfrrGgQjnLw2UGDxJQXc9P12fGTvD2q3Q3VaMI8TKKFqZXsQJBANufh1zfP+xX/UfxJ4QzDUCHCu2gnyTDj3nG9Bc80E5g7NwR2VBXF1R+QQCK9GZcXd2y6vBYgrHOSUiLbVjGrycCQQDpOcs0zbjUEUuTsQUT+fiO50dJSrZpus6ZFxz85sMppeItWSzsVeYWbW7adYnZ2Gu72OPjM/0xPYsXEakhHSRRAkAxlVauNQjthv/72god4pi/VL224GiNmEkwKSa6iFRPHbrcBHuXk9IElWx/ft+mrHvUraw1DwaStgv9gNzzCghJAkEA08RegCRnIzuGvgeejLk4suIeCMD/11AvmSvxbRWS5rq1leSVo7uGLSnqDbwlzE4dGb5kH15NNAp14/l2Fu/yZg==
@@ -40,8 +40,7 @@ class TestAddOnionResponse(unittest.TestCase):
     """
 
     # working case
-    response = test.util.get_message(WITH_PRIVATE_KEY)
-    stem.response.convert('ADD_ONION', response)
+    response = ControlMessage.from_str(WITH_PRIVATE_KEY, 'ADD_ONION', normalize = True)
 
     # now this should be a AddOnionResponse (ControlMessage subclass)
     self.assertTrue(isinstance(response, stem.response.ControlMessage))
@@ -57,9 +56,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response when there's a private key.
     """
 
-    response = test.util.get_message(WITH_PRIVATE_KEY)
-    stem.response.convert('ADD_ONION', response)
-
+    response = ControlMessage.from_str(WITH_PRIVATE_KEY, 'ADD_ONION', normalize = True)
     self.assertEqual('gfzprpioee3hoppz', response.service_id)
     self.assertTrue(response.private_key.startswith('MIICXgIBAAKB'))
     self.assertEqual('RSA1024', response.private_key_type)
@@ -70,9 +67,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response when there's client credentials.
     """
 
-    response = test.util.get_message(WITH_CLIENT_AUTH)
-    stem.response.convert('ADD_ONION', response)
-
+    response = ControlMessage.from_str(WITH_CLIENT_AUTH, 'ADD_ONION', normalize = True)
     self.assertEqual('oekn5sqrvcu4wote', response.service_id)
     self.assertEqual(None, response.private_key)
     self.assertEqual(None, response.private_key_type)
@@ -83,9 +78,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response without a private key.
     """
 
-    response = test.util.get_message(WITHOUT_PRIVATE_KEY)
-    stem.response.convert('ADD_ONION', response)
-
+    response = ControlMessage.from_str(WITHOUT_PRIVATE_KEY, 'ADD_ONION', normalize = True)
     self.assertEqual('gfzprpioee3hoppz', response.service_id)
     self.assertEqual(None, response.private_key)
     self.assertEqual(None, response.private_key_type)
@@ -95,7 +88,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response that lack an initial service id.
     """
 
-    response = test.util.get_message(WRONG_FIRST_KEY)
+    response = ControlMessage.from_str(WRONG_FIRST_KEY, normalize = True)
     self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION response should start with', stem.response.convert, 'ADD_ONION', response)
 
   def test_no_key_type(self):
@@ -103,5 +96,5 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response that's missing the private key type.
     """
 
-    response = test.util.get_message(MISSING_KEY_TYPE)
+    response = ControlMessage.from_str(MISSING_KEY_TYPE, normalize = True)
     self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION PrivateKey lines should be of the form', stem.response.convert, 'ADD_ONION', response)
diff --git a/test/unit/response/authchallenge.py b/test/unit/response/authchallenge.py
index 6a6533c..da195e1 100644
--- a/test/unit/response/authchallenge.py
+++ b/test/unit/response/authchallenge.py
@@ -8,7 +8,7 @@ import stem.response
 import stem.response.authchallenge
 import stem.socket
 
-import test.util
+from stem.response import ControlMessage
 
 VALID_RESPONSE = '250 AUTHCHALLENGE \
 SERVERHASH=B16F72DACD4B5ED1531F3FCC04B593D46A1E30267E636EA7C7F8DD7A2B7BAA05 \
@@ -27,8 +27,7 @@ class TestAuthChallengeResponse(unittest.TestCase):
     Parses valid AUTHCHALLENGE responses.
     """
 
-    control_message = test.util.get_message(VALID_RESPONSE)
-    stem.response.convert('AUTHCHALLENGE', control_message)
+    control_message = ControlMessage.from_str(VALID_RESPONSE, 'AUTHCHALLENGE', normalize = True)
 
     # now this should be a AuthChallengeResponse (ControlMessage subclass)
     self.assertTrue(isinstance(control_message, stem.response.ControlMessage))
@@ -51,5 +50,5 @@ class TestAuthChallengeResponse(unittest.TestCase):
       # constructed.
 
       remaining_comp = auth_challenge_comp[:index] + auth_challenge_comp[index + 1:]
-      control_message = test.util.get_message(' '.join(remaining_comp))
+      control_message = ControlMessage.from_str(' '.join(remaining_comp), normalize = True)
       self.assertRaises(stem.ProtocolError, stem.response.convert, 'AUTHCHALLENGE', control_message)
diff --git a/test/unit/response/events.py b/test/unit/response/events.py
index b14b739..9031463 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -10,9 +10,8 @@ import stem.response
 import stem.response.events
 import stem.util.log
 
-import test.util
-
 from stem import *  # enums and exceptions
+from stem.response import ControlMessage
 from stem.descriptor.router_status_entry import RouterStatusEntryV3
 
 try:
@@ -457,9 +456,7 @@ TB_EMPTY_BAD_2 = '650 TB_EMPTY GLOBAL READ=93 WRITTEN=93 LAST=-100'
 
 
 def _get_event(content):
-  controller_event = test.util.get_message(content)
-  stem.response.convert('EVENT', controller_event)
-  return controller_event
+  return ControlMessage.from_str(content, 'EVENT', normalize = True)
 
 
 class TestEvents(unittest.TestCase):
diff --git a/test/unit/response/getconf.py b/test/unit/response/getconf.py
index fb72ffc..c9765fc 100644
--- a/test/unit/response/getconf.py
+++ b/test/unit/response/getconf.py
@@ -8,9 +8,7 @@ import stem.response
 import stem.response.getconf
 import stem.socket
 
-import test.util
-
-EMPTY_RESPONSE = '250 OK'
+from stem.response import ControlMessage
 
 SINGLE_RESPONSE = """\
 250 DataDirectory=/home/neena/.tor"""
@@ -42,8 +40,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply without options (just calling "GETCONF").
     """
 
-    control_message = test.util.get_message(EMPTY_RESPONSE)
-    stem.response.convert('GETCONF', control_message)
+    control_message = ControlMessage.from_str('250 OK\r\n', 'GETCONF')
 
     # now this should be a GetConfResponse (ControlMessage subclass)
     self.assertTrue(isinstance(control_message, stem.response.ControlMessage))
@@ -56,8 +53,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply response for a single parameter.
     """
 
-    control_message = test.util.get_message(SINGLE_RESPONSE)
-    stem.response.convert('GETCONF', control_message)
+    control_message = ControlMessage.from_str(SINGLE_RESPONSE, 'GETCONF', normalize = True)
     self.assertEqual({'DataDirectory': ['/home/neena/.tor']}, control_message.entries)
 
   def test_batch_response(self):
@@ -65,9 +61,6 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply for muiltiple parameters.
     """
 
-    control_message = test.util.get_message(BATCH_RESPONSE)
-    stem.response.convert('GETCONF', control_message)
-
     expected = {
       'CookieAuthentication': ['0'],
       'ControlPort': ['9100'],
@@ -75,6 +68,7 @@ class TestGetConfResponse(unittest.TestCase):
       'DirPort': [],
     }
 
+    control_message = ControlMessage.from_str(BATCH_RESPONSE, 'GETCONF', normalize = True)
     self.assertEqual(expected, control_message.entries)
 
   def test_multivalue_response(self):
@@ -82,14 +76,12 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply containing a single key with multiple parameters.
     """
 
-    control_message = test.util.get_message(MULTIVALUE_RESPONSE)
-    stem.response.convert('GETCONF', control_message)
-
     expected = {
       'ControlPort': ['9100'],
       'ExitPolicy': ['accept 34.3.4.5', 'accept 3.4.53.3', 'accept 3.4.53.3', 'reject 23.245.54.3']
     }
 
+    control_message = ControlMessage.from_str(MULTIVALUE_RESPONSE, 'GETCONF', normalize = True)
     self.assertEqual(expected, control_message.entries)
 
   def test_unrecognized_key_response(self):
@@ -97,11 +89,10 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply that contains an error code with an unrecognized key.
     """
 
-    control_message = test.util.get_message(UNRECOGNIZED_KEY_RESPONSE)
-    self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETCONF', control_message)
-
     try:
+      control_message = ControlMessage.from_str(UNRECOGNIZED_KEY_RESPONSE, normalize = True)
       stem.response.convert('GETCONF', control_message)
+      self.fail('expected a stem.InvalidArguments to be raised')
     except stem.InvalidArguments as exc:
       self.assertEqual(exc.arguments, ['brickroad', 'submarine'])
 
@@ -112,5 +103,5 @@ class TestGetConfResponse(unittest.TestCase):
     GETCONF's spec.
     """
 
-    control_message = test.util.get_message(INVALID_RESPONSE)
+    control_message = ControlMessage.from_str(INVALID_RESPONSE, normalize = True)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETCONF', control_message)
diff --git a/test/unit/response/getinfo.py b/test/unit/response/getinfo.py
index 2054a03..b08e311 100644
--- a/test/unit/response/getinfo.py
+++ b/test/unit/response/getinfo.py
@@ -9,9 +9,7 @@ import stem.response.getinfo
 import stem.socket
 import stem.util.str_tools
 
-import test.util
-
-EMPTY_RESPONSE = '250 OK'
+from stem.response import ControlMessage
 
 SINGLE_RESPONSE = """\
 250-version=0.2.3.11-alpha-dev
@@ -57,8 +55,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply without options (just calling "GETINFO").
     """
 
-    control_message = test.util.get_message(EMPTY_RESPONSE)
-    stem.response.convert('GETINFO', control_message)
+    control_message = ControlMessage.from_str('250 OK\r\n', 'GETINFO')
 
     # now this should be a GetInfoResponse (ControlMessage subclass)
     self.assertTrue(isinstance(control_message, stem.response.ControlMessage))
@@ -71,8 +68,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply response for a single parameter.
     """
 
-    control_message = test.util.get_message(SINGLE_RESPONSE)
-    stem.response.convert('GETINFO', control_message)
+    control_message = ControlMessage.from_str(SINGLE_RESPONSE, 'GETINFO', normalize = True)
     self.assertEqual({'version': b'0.2.3.11-alpha-dev'}, control_message.entries)
 
   def test_batch_response(self):
@@ -80,15 +76,13 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply for muiltiple parameters.
     """
 
-    control_message = test.util.get_message(BATCH_RESPONSE)
-    stem.response.convert('GETINFO', control_message)
-
     expected = {
       'version': b'0.2.3.11-alpha-dev',
       'address': b'67.137.76.214',
       'fingerprint': b'5FDE0422045DF0E1879A3738D09099EB4A0C5BA0',
     }
 
+    control_message = ControlMessage.from_str(BATCH_RESPONSE, 'GETINFO', normalize = True)
     self.assertEqual(expected, control_message.entries)
 
   def test_multiline_response(self):
@@ -97,14 +91,12 @@ class TestGetInfoResponse(unittest.TestCase):
     value.
     """
 
-    control_message = test.util.get_message(MULTILINE_RESPONSE)
-    stem.response.convert('GETINFO', control_message)
-
     expected = {
       'version': b'0.2.3.11-alpha-dev (git-ef0bc7f8f26a917c)',
       'config-text': b'\n'.join(stem.util.str_tools._to_bytes(MULTILINE_RESPONSE).splitlines()[2:8]),
     }
 
+    control_message = ControlMessage.from_str(MULTILINE_RESPONSE, 'GETINFO', normalize = True)
     self.assertEqual(expected, control_message.entries)
 
   def test_invalid_non_mapping_content(self):
@@ -113,7 +105,7 @@ class TestGetInfoResponse(unittest.TestCase):
     entry.
     """
 
-    control_message = test.util.get_message(NON_KEY_VALUE_ENTRY)
+    control_message = ControlMessage.from_str(NON_KEY_VALUE_ENTRY, normalize = True)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETINFO', control_message)
 
   def test_unrecognized_key_response(self):
@@ -121,11 +113,10 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETCONF reply that contains an error code with an unrecognized key.
     """
 
-    control_message = test.util.get_message(UNRECOGNIZED_KEY_ENTRY)
-    self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETINFO', control_message)
-
     try:
+      control_message = ControlMessage.from_str(UNRECOGNIZED_KEY_ENTRY, normalize = True)
       stem.response.convert('GETINFO', control_message)
+      self.fail('expected a stem.InvalidArguments to be raised')
     except stem.InvalidArguments as exc:
       self.assertEqual(exc.arguments, ['blackhole'])
 
@@ -136,5 +127,5 @@ class TestGetInfoResponse(unittest.TestCase):
     malformed according to the GETINFO's spec.
     """
 
-    control_message = test.util.get_message(MISSING_MULTILINE_NEWLINE)
+    control_message = ControlMessage.from_str(MISSING_MULTILINE_NEWLINE, normalize = True)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETINFO', control_message)
diff --git a/test/unit/response/mapaddress.py b/test/unit/response/mapaddress.py
index 63813d4..7482507 100644
--- a/test/unit/response/mapaddress.py
+++ b/test/unit/response/mapaddress.py
@@ -8,9 +8,7 @@ import stem.response
 import stem.response.mapaddress
 import stem.socket
 
-import test.util
-
-SINGLE_RESPONSE = """250 foo=bar"""
+from stem.response import ControlMessage
 
 BATCH_RESPONSE = """\
 250-foo=bar
@@ -36,8 +34,7 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS reply response with a single address mapping.
     """
 
-    control_message = test.util.get_message(SINGLE_RESPONSE)
-    stem.response.convert('MAPADDRESS', control_message)
+    control_message = ControlMessage.from_str('250 foo=bar\r\n', 'MAPADDRESS')
     self.assertEqual({'foo': 'bar'}, control_message.entries)
 
   def test_batch_response(self):
@@ -45,9 +42,6 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS reply with multiple address mappings
     """
 
-    control_message = test.util.get_message(BATCH_RESPONSE)
-    stem.response.convert('MAPADDRESS', control_message)
-
     expected = {
       'foo': 'bar',
       'baz': 'quux',
@@ -55,6 +49,7 @@ class TestMapAddressResponse(unittest.TestCase):
       '120.23.23.2': 'torproject.org'
     }
 
+    control_message = ControlMessage.from_str(BATCH_RESPONSE, 'MAPADDRESS', normalize = True)
     self.assertEqual(expected, control_message.entries)
 
   def test_invalid_requests(self):
@@ -62,13 +57,11 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS replies that contain an error code due to hostname syntax errors.
     """
 
-    control_message = test.util.get_message(UNRECOGNIZED_KEYS_RESPONSE)
+    control_message = ControlMessage.from_str(UNRECOGNIZED_KEYS_RESPONSE, normalize = True)
     self.assertRaises(stem.InvalidRequest, stem.response.convert, 'MAPADDRESS', control_message)
-    expected = {'23': '324'}
 
-    control_message = test.util.get_message(PARTIAL_FAILURE_RESPONSE)
-    stem.response.convert('MAPADDRESS', control_message)
-    self.assertEqual(expected, control_message.entries)
+    control_message = ControlMessage.from_str(PARTIAL_FAILURE_RESPONSE, 'MAPADDRESS', normalize = True)
+    self.assertEqual({'23': '324'}, control_message.entries)
 
   def test_invalid_response(self):
     """
@@ -77,8 +70,8 @@ class TestMapAddressResponse(unittest.TestCase):
     MAPADDRESS's spec.
     """
 
-    control_message = test.util.get_message(INVALID_EMPTY_RESPONSE)
+    control_message = ControlMessage.from_str(INVALID_EMPTY_RESPONSE, normalize = True)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'MAPADDRESS', control_message)
 
-    control_message = test.util.get_message(INVALID_RESPONSE)
+    control_message = ControlMessage.from_str(INVALID_RESPONSE, normalize = True)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'MAPADDRESS', control_message)
diff --git a/test/unit/response/protocolinfo.py b/test/unit/response/protocolinfo.py
index d8fb762..9b2c9b1 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -12,8 +12,7 @@ import stem.util.proc
 import stem.util.system
 import stem.version
 
-import test.util
-
+from stem.response import ControlMessage
 from stem.response.protocolinfo import AuthMethod
 
 try:
@@ -71,8 +70,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     """
 
     # working case
-    control_message = test.util.get_message(NO_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(NO_AUTH, 'PROTOCOLINFO', normalize = True)
 
     # now this should be a ProtocolInfoResponse (ControlMessage subclass)
     self.assertTrue(isinstance(control_message, stem.response.ControlMessage))
@@ -87,17 +85,14 @@ class TestProtocolInfoResponse(unittest.TestCase):
     self.assertRaises(TypeError, stem.response.convert, 'PROTOCOLINFO', 'hello world')
 
     # attempt to convert a different message type
-    bw_event_control_message = test.util.get_message('650 BW 32326 2856')
-    self.assertRaises(stem.ProtocolError, stem.response.convert, 'PROTOCOLINFO', bw_event_control_message)
+    self.assertRaises(stem.ProtocolError, ControlMessage.from_str, '650 BW 32326 2856\r\n', 'PROTOCOLINFO')
 
   def test_no_auth(self):
     """
     Checks a response when there's no authentication.
     """
 
-    control_message = test.util.get_message(NO_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
-
+    control_message = ControlMessage.from_str(NO_AUTH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual(1, control_message.protocol_version)
     self.assertEqual(stem.version.Version('0.2.1.30'), control_message.tor_version)
     self.assertEqual((AuthMethod.NONE, ), control_message.auth_methods)
@@ -109,8 +104,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with password authentication.
     """
 
-    control_message = test.util.get_message(PASSWORD_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(PASSWORD_AUTH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual((AuthMethod.PASSWORD, ), control_message.auth_methods)
 
   def test_cookie_auth(self):
@@ -119,8 +113,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     characters.
     """
 
-    control_message = test.util.get_message(COOKIE_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(COOKIE_AUTH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual((AuthMethod.COOKIE, ), control_message.auth_methods)
     self.assertEqual('/tmp/my data\\"dir//control_auth_cookie', control_message.cookie_path)
 
@@ -129,8 +122,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with multiple authentication methods.
     """
 
-    control_message = test.util.get_message(MULTIPLE_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(MULTIPLE_AUTH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual((AuthMethod.COOKIE, AuthMethod.PASSWORD), control_message.auth_methods)
     self.assertEqual('/home/atagar/.tor/control_auth_cookie', control_message.cookie_path)
 
@@ -139,8 +131,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with an unrecognized authtentication method.
     """
 
-    control_message = test.util.get_message(UNKNOWN_AUTH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(UNKNOWN_AUTH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual((AuthMethod.UNKNOWN, AuthMethod.PASSWORD), control_message.auth_methods)
     self.assertEqual(('MAGIC', 'PIXIE_DUST'), control_message.unknown_auth_methods)
 
@@ -150,9 +141,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     information to be a valid response.
     """
 
-    control_message = test.util.get_message(MINIMUM_RESPONSE)
-    stem.response.convert('PROTOCOLINFO', control_message)
-
+    control_message = ControlMessage.from_str(MINIMUM_RESPONSE, 'PROTOCOLINFO', normalize = True)
     self.assertEqual(5, control_message.protocol_version)
     self.assertEqual(None, control_message.tor_version)
     self.assertEqual((), control_message.auth_methods)
@@ -165,8 +154,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks an authentication cookie with a unicode path.
     """
 
-    control_message = test.util.get_message(UNICODE_COOKIE_PATH)
-    stem.response.convert('PROTOCOLINFO', control_message)
+    control_message = ControlMessage.from_str(UNICODE_COOKIE_PATH, 'PROTOCOLINFO', normalize = True)
     self.assertEqual(EXPECTED_UNICODE_PATH, control_message.cookie_path)
 
   @patch('stem.util.proc.is_available', Mock(return_value = False))
@@ -191,9 +179,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     with patch('stem.util.system.call') as call_mock:
       call_mock.side_effect = call_function
 
-      control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
-      stem.response.convert('PROTOCOLINFO', control_message)
-
+      control_message = ControlMessage.from_str(RELATIVE_COOKIE_PATH, 'PROTOCOLINFO', normalize = True)
       stem.connection._expand_cookie_path(control_message, stem.util.system.pid_by_name, 'tor')
 
       self.assertEqual(os.path.join('/tmp/foo', 'tor-browser_en-US', 'Data', 'control_auth_cookie'), control_message.cookie_path)
@@ -202,6 +188,5 @@ class TestProtocolInfoResponse(unittest.TestCase):
     # leaving the path unexpanded)
 
     with patch('stem.util.system.call', Mock(return_value = None)):
-      control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
-      stem.response.convert('PROTOCOLINFO', control_message)
+      control_message = ControlMessage.from_str(RELATIVE_COOKIE_PATH, 'PROTOCOLINFO', normalize = True)
       self.assertEqual('./tor-browser_en-US/Data/control_auth_cookie', control_message.cookie_path)
diff --git a/test/unit/response/singleline.py b/test/unit/response/singleline.py
index 1c05aa5..76b252d 100644
--- a/test/unit/response/singleline.py
+++ b/test/unit/response/singleline.py
@@ -4,33 +4,24 @@ Unit tests for the stem.response.SingleLineResponse class.
 
 import unittest
 
-import stem.response
-import stem.socket
+import stem
 
-import test.util
-
-MULTILINE_RESPONSE = """250-MULTI
-250 LINE"""
+from stem.response import ControlMessage
 
 
 class TestSingleLineResponse(unittest.TestCase):
   def test_single_line_response(self):
-    message = test.util.get_message('552 NOTOK')
-    stem.response.convert('SINGLELINE', message)
+    message = ControlMessage.from_str('552 NOTOK\r\n', 'SINGLELINE')
     self.assertEqual(False, message.is_ok())
 
-    message = test.util.get_message('250 KK')
-    stem.response.convert('SINGLELINE', message)
+    message = ControlMessage.from_str('250 KK\r\n', 'SINGLELINE')
     self.assertEqual(True, message.is_ok())
 
-    message = test.util.get_message('250 OK')
-    stem.response.convert('SINGLELINE', message)
+    message = ControlMessage.from_str('250 OK\r\n', 'SINGLELINE')
     self.assertEqual(True, message.is_ok(True))
 
-    message = test.util.get_message('250 HMM')
-    stem.response.convert('SINGLELINE', message)
+    message = ControlMessage.from_str('250 HMM\r\n', 'SINGLELINE')
     self.assertEqual(False, message.is_ok(True))
 
   def test_multi_line_response(self):
-    message = test.util.get_message(MULTILINE_RESPONSE)
-    self.assertRaises(stem.ProtocolError, stem.response.convert, 'SINGLELINE', message)
+    self.assertRaises(stem.ProtocolError, ControlMessage.from_str, '250-MULTI\r\n250 LINE\r\n', 'SINGLELINE')
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index 2075bdf..50e766d 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -15,14 +15,13 @@ import stem.response
 import stem.descriptor.remote
 import stem.prereq
 
-import test.util
-
 from stem.control import Controller
-from stem.util import str_type
 from stem.descriptor.networkstatus import NetworkStatusDocumentV3
 from stem.descriptor.remote import DIRECTORY_AUTHORITIES
 from stem.descriptor.router_status_entry import ROUTER_STATUS_ENTRY_V3_HEADER, RouterStatusEntryV3
 from stem.descriptor.server_descriptor import RelayDescriptor
+from stem.response import ControlMessage
+from stem.util import str_type
 
 from test.unit import exec_documentation_example
 
@@ -100,9 +99,7 @@ A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB: caerSidi
 
 
 def _get_event(content):
-  controller_event = test.util.get_message(content)
-  stem.response.convert('EVENT', controller_event)
-  return controller_event
+  return ControlMessage.from_str(content, 'EVENT', normalize = True)
 
 
 def _get_circ_event(id, status, hop1, hop2, hop3, purpose):
diff --git a/test/util.py b/test/util.py
index 47d90fd..7ad309d 100644
--- a/test/util.py
+++ b/test/util.py
@@ -12,7 +12,6 @@ Helper functions for our test framework.
   get_prereq - provides the tor version required to run the given target
   get_torrc_entries - provides the torrc entries for a given target
 
-  get_message - provides a ControlMessage instance
   get_protocolinfo_response - provides a ProtocolInfoResponse instance
   get_all_combinations - provides all combinations of attributes
   random_fingerprint - provides a random relay fingerprint
@@ -21,7 +20,6 @@ Helper functions for our test framework.
 
 import hashlib
 import itertools
-import re
 import os
 
 import stem
@@ -250,29 +248,6 @@ def random_fingerprint():
   return hashlib.sha1(os.urandom(20)).hexdigest().upper()
 
 
-def get_message(content, reformat = True):
-  """
-  Provides a ControlMessage with content modified to be parsable. This makes
-  the following changes unless 'reformat' is false...
-
-  * ensures the content ends with a newline
-  * newlines are replaced with a carriage return and newline pair
-
-  :param str content: base content for the controller message
-  :param str reformat: modifies content to be more accommodating to being parsed
-
-  :returns: stem.response.ControlMessage instance
-  """
-
-  if reformat:
-    if not content.endswith('\n'):
-      content += '\n'
-
-    content = re.sub('([\r]?)\n', '\r\n', content)
-
-  return stem.response.ControlMessage.from_str(content)
-
-
 def get_protocolinfo_response(**attributes):
   """
   Provides a ProtocolInfoResponse, customized with the given attributes. The
@@ -284,7 +259,7 @@ def get_protocolinfo_response(**attributes):
   :returns: stem.response.protocolinfo.ProtocolInfoResponse instance
   """
 
-  protocolinfo_response = get_message('250-PROTOCOLINFO 1\n250 OK')
+  protocolinfo_response = stem.response.ControlMessage.from_str('250-PROTOCOLINFO 1\r\n250 OK\r\n', 'PROTOCOLINFO')
   stem.response.convert('PROTOCOLINFO', protocolinfo_response)
 
   for attr in attributes:





More information about the tor-commits mailing list