[tor-commits] [stem/master] Merge remaining mocking module into util

atagar at torproject.org atagar at torproject.org
Tue May 2 19:57:29 UTC 2017


commit b164478ea9c0275bc668cef32e84f4026c05a246
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue May 2 09:37:47 2017 -0700

    Merge remaining mocking module into util
    
    Without descriptor creation there's really not much left. Merging the few
    remaining helpers into the util module.
---
 test/__init__.py                       |   3 +-
 test/integ/control/base_controller.py  |   4 +-
 test/integ/control/controller.py       |  10 +--
 test/integ/socket/control_message.py   |   4 +-
 test/mocking.py                        | 118 --------------------------------
 test/unit/connection/authentication.py |   9 +--
 test/unit/control/controller.py        |   7 +-
 test/unit/doctest.py                   |   3 +-
 test/unit/interpreter/commands.py      |  11 +--
 test/unit/response/add_onion.py        |  14 ++--
 test/unit/response/authchallenge.py    |   6 +-
 test/unit/response/events.py           |   5 +-
 test/unit/response/getconf.py          |  14 ++--
 test/unit/response/getinfo.py          |  16 ++---
 test/unit/response/mapaddress.py       |  14 ++--
 test/unit/response/protocolinfo.py     |  25 +++----
 test/unit/response/singleline.py       |  12 ++--
 test/unit/tutorial_examples.py         |   5 +-
 test/unit/util/proc.py                 |   5 +-
 test/util.py                           | 120 ++++++++++++++++++++++++++++++---
 20 files changed, 195 insertions(+), 210 deletions(-)

diff --git a/test/__init__.py b/test/__init__.py
index 7ab0853..8ea70d2 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -6,10 +6,9 @@ Unit and integration tests for the stem library.
 """
 
 __all__ = [
-  'mocking',
   'network',
   'output',
   'prompt',
   'runner',
-  'utils',
+  'util',
 ]
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index 0a51e1f..1e64445 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -11,8 +11,8 @@ import stem.control
 import stem.socket
 import stem.util.system
 
-import test.mocking
 import test.runner
+import test.util
 
 from test.util import require_controller
 
@@ -154,7 +154,7 @@ class TestBaseController(unittest.TestCase):
       controller.msg('SETEVENTS CONF_CHANGED')
 
       for i in range(10):
-        controller.msg('SETCONF NodeFamily=%s' % test.mocking.random_fingerprint())
+        controller.msg('SETCONF NodeFamily=%s' % test.util.random_fingerprint())
         test.runner.exercise_controller(self, controller)
 
       controller.msg('SETEVENTS')
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 48a50fe..895d435 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -18,7 +18,6 @@ import stem.response.protocolinfo
 import stem.socket
 import stem.util.str_tools
 import stem.version
-import test.mocking
 import test.network
 import test.runner
 
@@ -29,6 +28,7 @@ from stem.version import Requirement
 
 from test.util import (
   register_new_capability,
+  random_fingerprint,
   tor_version,
   only_run_once,
   require_controller,
@@ -150,7 +150,7 @@ class TestController(unittest.TestCase):
       controller.add_event_listener(listener2, EventType.CONF_CHANGED, EventType.DEBUG)
 
       # The NodeFamily is a harmless option we can toggle
-      controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+      controller.set_conf('NodeFamily', random_fingerprint())
 
       # Wait for the event. Assert that we get it within 10 seconds
       event_notice1.wait(10)
@@ -167,7 +167,7 @@ class TestController(unittest.TestCase):
 
       buffer2_size = len(event_buffer2)
 
-      controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+      controller.set_conf('NodeFamily', random_fingerprint())
       event_notice1.wait(10)
       self.assertEqual(len(event_buffer1), 2)
       event_notice1.clear()
@@ -204,7 +204,7 @@ class TestController(unittest.TestCase):
 
       # trigger an event
 
-      controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+      controller.set_conf('NodeFamily', random_fingerprint())
       event_notice.wait(4)
       self.assertTrue(len(event_buffer) >= 1)
 
@@ -217,7 +217,7 @@ class TestController(unittest.TestCase):
       controller.connect()
       controller.authenticate(password = test.runner.CONTROL_PASSWORD)
       self.assertTrue(len(event_buffer) == 0)
-      controller.set_conf('NodeFamily', test.mocking.random_fingerprint())
+      controller.set_conf('NodeFamily', random_fingerprint())
 
       event_notice.wait(4)
       self.assertTrue(len(event_buffer) >= 1)
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index 13075ca..646f732 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -7,12 +7,12 @@ import unittest
 
 import stem.socket
 import stem.version
-import test.mocking
 import test.runner
 
 from test.util import (
   require_controller,
   require_version,
+  random_fingerprint,
 )
 
 
@@ -162,7 +162,7 @@ class TestControlMessage(unittest.TestCase):
       # We'll receive both a CONF_CHANGED event and 'OK' response for the
       # SETCONF, but not necessarily in any specific order.
 
-      control_socket.send('SETCONF NodeFamily=%s' % test.mocking.random_fingerprint())
+      control_socket.send('SETCONF NodeFamily=%s' % random_fingerprint())
       msg1 = control_socket.recv()
       msg2 = control_socket.recv()
 
diff --git a/test/mocking.py b/test/mocking.py
deleted file mode 100644
index 6369598..0000000
--- a/test/mocking.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2012-2017, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Helper functions for creating mock objects.
-
-::
-
-  get_all_combinations - provides all combinations of attributes
-  random_fingerprint - provides a random relay fingerprint
-
-  Instance Constructors
-    get_message                     - stem.response.ControlMessage
-    get_protocolinfo_response       - stem.response.protocolinfo.ProtocolInfoResponse
-"""
-
-import hashlib
-import itertools
-import os
-import re
-
-import stem.descriptor.extrainfo_descriptor
-import stem.descriptor.hidden_service_descriptor
-import stem.descriptor.microdescriptor
-import stem.descriptor.networkstatus
-import stem.descriptor.router_status_entry
-import stem.descriptor.server_descriptor
-import stem.prereq
-import stem.response
-import stem.util.str_tools
-
-
-def get_all_combinations(attr, include_empty = False):
-  """
-  Provides an iterator for all combinations of a set of attributes. For
-  instance...
-
-  ::
-
-    >>> list(test.mocking.get_all_combinations(['a', 'b', 'c']))
-    [('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]
-
-  :param list attr: attributes to provide combinations for
-  :param bool include_empty: includes an entry with zero items if True
-  :returns: iterator for all combinations
-  """
-
-  # Makes an itertools.product() call for 'i' copies of attr...
-  #
-  # * itertools.product(attr) => all one-element combinations
-  # * itertools.product(attr, attr) => all two-element combinations
-  # * ... etc
-
-  if include_empty:
-    yield ()
-
-  seen = set()
-  for index in range(1, len(attr) + 1):
-    product_arg = [attr for _ in range(index)]
-
-    for item in itertools.product(*product_arg):
-      # deduplicate, sort, and only provide if we haven't seen it yet
-      item = tuple(sorted(set(item)))
-
-      if item not in seen:
-        seen.add(item)
-        yield item
-
-
-def random_fingerprint():
-  """
-  Provides a random relay 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
-  base instance is minimal, with its version set to one and everything else
-  left with the default.
-
-  :param dict attributes: attributes to customize the response with
-
-  :returns: stem.response.protocolinfo.ProtocolInfoResponse instance
-  """
-
-  protocolinfo_response = get_message('250-PROTOCOLINFO 1\n250 OK')
-  stem.response.convert('PROTOCOLINFO', protocolinfo_response)
-
-  for attr in attributes:
-    setattr(protocolinfo_response, attr, attributes[attr])
-
-  return protocolinfo_response
diff --git a/test/unit/connection/authentication.py b/test/unit/connection/authentication.py
index b7ccc0d..044f163 100644
--- a/test/unit/connection/authentication.py
+++ b/test/unit/connection/authentication.py
@@ -13,8 +13,9 @@ import unittest
 
 import stem.connection
 
+import test.util
+
 from stem.util import log
-from test import mocking
 
 try:
   # added in python 3.3
@@ -33,7 +34,7 @@ class TestAuthenticate(unittest.TestCase):
 
     # tests where get_protocolinfo succeeds
 
-    get_protocolinfo_mock.return_value = mocking.get_protocolinfo_response(
+    get_protocolinfo_mock.return_value = test.util.get_protocolinfo_response(
       auth_methods = (stem.connection.AuthMethod.NONE, ),
     )
 
@@ -85,7 +86,7 @@ class TestAuthenticate(unittest.TestCase):
       stem.connection.AuthChallengeFailed(None, None),
       stem.ControllerError(None))
 
-    auth_method_combinations = mocking.get_all_combinations([
+    auth_method_combinations = test.util.get_all_combinations([
       stem.connection.AuthMethod.NONE,
       stem.connection.AuthMethod.PASSWORD,
       stem.connection.AuthMethod.COOKIE,
@@ -93,7 +94,7 @@ class TestAuthenticate(unittest.TestCase):
       stem.connection.AuthMethod.UNKNOWN,
     ], include_empty = True)
 
-    protocolinfo = mocking.get_protocolinfo_response(cookie_path = '/tmp/blah')
+    protocolinfo = test.util.get_protocolinfo_response(cookie_path = '/tmp/blah')
 
     for auth_methods in auth_method_combinations:
       for auth_none_exc in all_auth_none_exc:
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 47ac9d2..2cf8b7e 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -13,10 +13,11 @@ import stem.socket
 import stem.util.system
 import stem.version
 
+import test.util
+
 from stem import ControllerError, DescriptorUnavailable, InvalidArguments, InvalidRequest, ProtocolError, UnsatisfiableRequest
 from stem.control import _parse_circ_path, Listener, Controller, EventType
 from stem.exit_policy import ExitPolicy
-from test import mocking
 
 try:
   # added in python 3.3
@@ -338,13 +339,13 @@ class TestControl(unittest.TestCase):
 
     # use the handy mocked protocolinfo response
 
-    get_protocolinfo_mock.return_value = mocking.get_protocolinfo_response()
+    get_protocolinfo_mock.return_value = test.util.get_protocolinfo_response()
 
     # compare the str representation of these object, because the class
     # does not have, nor need, a direct comparison operator
 
     self.assertEqual(
-      str(mocking.get_protocolinfo_response()),
+      str(test.util.get_protocolinfo_response()),
       str(self.controller.get_protocolinfo())
     )
 
diff --git a/test/unit/doctest.py b/test/unit/doctest.py
index 548831a..06a6eaa 100644
--- a/test/unit/doctest.py
+++ b/test/unit/doctest.py
@@ -14,7 +14,6 @@ import stem.util.str_tools
 import stem.util.system
 import stem.version
 
-import test.mocking
 import test.util
 
 try:
@@ -84,7 +83,7 @@ class TestDocumentation(unittest.TestCase):
           'circuit-status': EXPECTED_CIRCUIT_STATUS,
         }[arg]
 
-        response = test.mocking.get_message(ADD_ONION_RESPONSE)
+        response = test.util.get_message(ADD_ONION_RESPONSE)
         stem.response.convert('ADD_ONION', response)
         controller.create_ephemeral_hidden_service.return_value = response
 
diff --git a/test/unit/interpreter/commands.py b/test/unit/interpreter/commands.py
index 63fedc5..c8cbc09 100644
--- a/test/unit/interpreter/commands.py
+++ b/test/unit/interpreter/commands.py
@@ -5,9 +5,10 @@ import stem
 import stem.response
 import stem.version
 
+import test.util
+
 from stem.interpreter.commands import ControlInterpreter, _get_fingerprint
 
-from test import mocking
 from test.unit.interpreter import CONTROLLER
 
 try:
@@ -125,7 +126,7 @@ class TestInterpreterCommands(unittest.TestCase):
     )
 
     for content in event_contents:
-      event = mocking.get_message(content)
+      event = test.util.get_message(content)
       stem.response.convert('EVENT', event)
       interpreter._received_events.append(event)
 
@@ -170,7 +171,7 @@ class TestInterpreterCommands(unittest.TestCase):
     response = '250-version=0.2.5.1-alpha-dev (git-245ecfff36c0cecc)\r\n250 OK'
 
     controller = Mock()
-    controller.msg.return_value = mocking.get_message(response)
+    controller.msg.return_value = test.util.get_message(response)
 
     interpreter = ControlInterpreter(controller)
 
@@ -185,7 +186,7 @@ class TestInterpreterCommands(unittest.TestCase):
     response = '250-Log=notice stdout\r\n250 Address'
 
     controller = Mock()
-    controller.msg.return_value = mocking.get_message(response)
+    controller.msg.return_value = test.util.get_message(response)
 
     interpreter = ControlInterpreter(controller)
 
@@ -194,7 +195,7 @@ class TestInterpreterCommands(unittest.TestCase):
 
   def test_setevents(self):
     controller = Mock()
-    controller.msg.return_value = mocking.get_message('250 OK')
+    controller.msg.return_value = test.util.get_message('250 OK')
 
     interpreter = ControlInterpreter(controller)
 
diff --git a/test/unit/response/add_onion.py b/test/unit/response/add_onion.py
index 1213f0c..048ca67 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
 
-from test import mocking
+import test.util
 
 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,7 +40,7 @@ class TestAddOnionResponse(unittest.TestCase):
     """
 
     # working case
-    response = mocking.get_message(WITH_PRIVATE_KEY)
+    response = test.util.get_message(WITH_PRIVATE_KEY)
     stem.response.convert('ADD_ONION', response)
 
     # now this should be a AddOnionResponse (ControlMessage subclass)
@@ -57,7 +57,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response when there's a private key.
     """
 
-    response = mocking.get_message(WITH_PRIVATE_KEY)
+    response = test.util.get_message(WITH_PRIVATE_KEY)
     stem.response.convert('ADD_ONION', response)
 
     self.assertEqual('gfzprpioee3hoppz', response.service_id)
@@ -70,7 +70,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response when there's client credentials.
     """
 
-    response = mocking.get_message(WITH_CLIENT_AUTH)
+    response = test.util.get_message(WITH_CLIENT_AUTH)
     stem.response.convert('ADD_ONION', response)
 
     self.assertEqual('oekn5sqrvcu4wote', response.service_id)
@@ -83,7 +83,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response without a private key.
     """
 
-    response = mocking.get_message(WITHOUT_PRIVATE_KEY)
+    response = test.util.get_message(WITHOUT_PRIVATE_KEY)
     stem.response.convert('ADD_ONION', response)
 
     self.assertEqual('gfzprpioee3hoppz', response.service_id)
@@ -95,7 +95,7 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response that lack an initial service id.
     """
 
-    response = mocking.get_message(WRONG_FIRST_KEY)
+    response = test.util.get_message(WRONG_FIRST_KEY)
     self.assertRaisesRegexp(stem.ProtocolError, 'ADD_ONION response should start with', stem.response.convert, 'ADD_ONION', response)
 
   def test_no_key_type(self):
@@ -103,5 +103,5 @@ class TestAddOnionResponse(unittest.TestCase):
     Checks a response that's missing the private key type.
     """
 
-    response = mocking.get_message(MISSING_KEY_TYPE)
+    response = test.util.get_message(MISSING_KEY_TYPE)
     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 2185ea8..6a6533c 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
 
-from test import mocking
+import test.util
 
 VALID_RESPONSE = '250 AUTHCHALLENGE \
 SERVERHASH=B16F72DACD4B5ED1531F3FCC04B593D46A1E30267E636EA7C7F8DD7A2B7BAA05 \
@@ -27,7 +27,7 @@ class TestAuthChallengeResponse(unittest.TestCase):
     Parses valid AUTHCHALLENGE responses.
     """
 
-    control_message = mocking.get_message(VALID_RESPONSE)
+    control_message = test.util.get_message(VALID_RESPONSE)
     stem.response.convert('AUTHCHALLENGE', control_message)
 
     # now this should be a AuthChallengeResponse (ControlMessage subclass)
@@ -51,5 +51,5 @@ class TestAuthChallengeResponse(unittest.TestCase):
       # constructed.
 
       remaining_comp = auth_challenge_comp[:index] + auth_challenge_comp[index + 1:]
-      control_message = mocking.get_message(' '.join(remaining_comp))
+      control_message = test.util.get_message(' '.join(remaining_comp))
       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 6023db6..b14b739 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -10,9 +10,10 @@ import stem.response
 import stem.response.events
 import stem.util.log
 
+import test.util
+
 from stem import *  # enums and exceptions
 from stem.descriptor.router_status_entry import RouterStatusEntryV3
-from test import mocking
 
 try:
   # added in python 3.3
@@ -456,7 +457,7 @@ TB_EMPTY_BAD_2 = '650 TB_EMPTY GLOBAL READ=93 WRITTEN=93 LAST=-100'
 
 
 def _get_event(content):
-  controller_event = mocking.get_message(content)
+  controller_event = test.util.get_message(content)
   stem.response.convert('EVENT', controller_event)
   return controller_event
 
diff --git a/test/unit/response/getconf.py b/test/unit/response/getconf.py
index 07c89fd..fb72ffc 100644
--- a/test/unit/response/getconf.py
+++ b/test/unit/response/getconf.py
@@ -8,7 +8,7 @@ import stem.response
 import stem.response.getconf
 import stem.socket
 
-from test import mocking
+import test.util
 
 EMPTY_RESPONSE = '250 OK'
 
@@ -42,7 +42,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply without options (just calling "GETCONF").
     """
 
-    control_message = mocking.get_message(EMPTY_RESPONSE)
+    control_message = test.util.get_message(EMPTY_RESPONSE)
     stem.response.convert('GETCONF', control_message)
 
     # now this should be a GetConfResponse (ControlMessage subclass)
@@ -56,7 +56,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply response for a single parameter.
     """
 
-    control_message = mocking.get_message(SINGLE_RESPONSE)
+    control_message = test.util.get_message(SINGLE_RESPONSE)
     stem.response.convert('GETCONF', control_message)
     self.assertEqual({'DataDirectory': ['/home/neena/.tor']}, control_message.entries)
 
@@ -65,7 +65,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply for muiltiple parameters.
     """
 
-    control_message = mocking.get_message(BATCH_RESPONSE)
+    control_message = test.util.get_message(BATCH_RESPONSE)
     stem.response.convert('GETCONF', control_message)
 
     expected = {
@@ -82,7 +82,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply containing a single key with multiple parameters.
     """
 
-    control_message = mocking.get_message(MULTIVALUE_RESPONSE)
+    control_message = test.util.get_message(MULTIVALUE_RESPONSE)
     stem.response.convert('GETCONF', control_message)
 
     expected = {
@@ -97,7 +97,7 @@ class TestGetConfResponse(unittest.TestCase):
     Parses a GETCONF reply that contains an error code with an unrecognized key.
     """
 
-    control_message = mocking.get_message(UNRECOGNIZED_KEY_RESPONSE)
+    control_message = test.util.get_message(UNRECOGNIZED_KEY_RESPONSE)
     self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETCONF', control_message)
 
     try:
@@ -112,5 +112,5 @@ class TestGetConfResponse(unittest.TestCase):
     GETCONF's spec.
     """
 
-    control_message = mocking.get_message(INVALID_RESPONSE)
+    control_message = test.util.get_message(INVALID_RESPONSE)
     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 afd0748..2054a03 100644
--- a/test/unit/response/getinfo.py
+++ b/test/unit/response/getinfo.py
@@ -9,7 +9,7 @@ import stem.response.getinfo
 import stem.socket
 import stem.util.str_tools
 
-from test import mocking
+import test.util
 
 EMPTY_RESPONSE = '250 OK'
 
@@ -57,7 +57,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply without options (just calling "GETINFO").
     """
 
-    control_message = mocking.get_message(EMPTY_RESPONSE)
+    control_message = test.util.get_message(EMPTY_RESPONSE)
     stem.response.convert('GETINFO', control_message)
 
     # now this should be a GetInfoResponse (ControlMessage subclass)
@@ -71,7 +71,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply response for a single parameter.
     """
 
-    control_message = mocking.get_message(SINGLE_RESPONSE)
+    control_message = test.util.get_message(SINGLE_RESPONSE)
     stem.response.convert('GETINFO', control_message)
     self.assertEqual({'version': b'0.2.3.11-alpha-dev'}, control_message.entries)
 
@@ -80,7 +80,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETINFO reply for muiltiple parameters.
     """
 
-    control_message = mocking.get_message(BATCH_RESPONSE)
+    control_message = test.util.get_message(BATCH_RESPONSE)
     stem.response.convert('GETINFO', control_message)
 
     expected = {
@@ -97,7 +97,7 @@ class TestGetInfoResponse(unittest.TestCase):
     value.
     """
 
-    control_message = mocking.get_message(MULTILINE_RESPONSE)
+    control_message = test.util.get_message(MULTILINE_RESPONSE)
     stem.response.convert('GETINFO', control_message)
 
     expected = {
@@ -113,7 +113,7 @@ class TestGetInfoResponse(unittest.TestCase):
     entry.
     """
 
-    control_message = mocking.get_message(NON_KEY_VALUE_ENTRY)
+    control_message = test.util.get_message(NON_KEY_VALUE_ENTRY)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'GETINFO', control_message)
 
   def test_unrecognized_key_response(self):
@@ -121,7 +121,7 @@ class TestGetInfoResponse(unittest.TestCase):
     Parses a GETCONF reply that contains an error code with an unrecognized key.
     """
 
-    control_message = mocking.get_message(UNRECOGNIZED_KEY_ENTRY)
+    control_message = test.util.get_message(UNRECOGNIZED_KEY_ENTRY)
     self.assertRaises(stem.InvalidArguments, stem.response.convert, 'GETINFO', control_message)
 
     try:
@@ -136,5 +136,5 @@ class TestGetInfoResponse(unittest.TestCase):
     malformed according to the GETINFO's spec.
     """
 
-    control_message = mocking.get_message(MISSING_MULTILINE_NEWLINE)
+    control_message = test.util.get_message(MISSING_MULTILINE_NEWLINE)
     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 753f2e6..63813d4 100644
--- a/test/unit/response/mapaddress.py
+++ b/test/unit/response/mapaddress.py
@@ -8,7 +8,7 @@ import stem.response
 import stem.response.mapaddress
 import stem.socket
 
-from test import mocking
+import test.util
 
 SINGLE_RESPONSE = """250 foo=bar"""
 
@@ -36,7 +36,7 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS reply response with a single address mapping.
     """
 
-    control_message = mocking.get_message(SINGLE_RESPONSE)
+    control_message = test.util.get_message(SINGLE_RESPONSE)
     stem.response.convert('MAPADDRESS', control_message)
     self.assertEqual({'foo': 'bar'}, control_message.entries)
 
@@ -45,7 +45,7 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS reply with multiple address mappings
     """
 
-    control_message = mocking.get_message(BATCH_RESPONSE)
+    control_message = test.util.get_message(BATCH_RESPONSE)
     stem.response.convert('MAPADDRESS', control_message)
 
     expected = {
@@ -62,11 +62,11 @@ class TestMapAddressResponse(unittest.TestCase):
     Parses a MAPADDRESS replies that contain an error code due to hostname syntax errors.
     """
 
-    control_message = mocking.get_message(UNRECOGNIZED_KEYS_RESPONSE)
+    control_message = test.util.get_message(UNRECOGNIZED_KEYS_RESPONSE)
     self.assertRaises(stem.InvalidRequest, stem.response.convert, 'MAPADDRESS', control_message)
     expected = {'23': '324'}
 
-    control_message = mocking.get_message(PARTIAL_FAILURE_RESPONSE)
+    control_message = test.util.get_message(PARTIAL_FAILURE_RESPONSE)
     stem.response.convert('MAPADDRESS', control_message)
     self.assertEqual(expected, control_message.entries)
 
@@ -77,8 +77,8 @@ class TestMapAddressResponse(unittest.TestCase):
     MAPADDRESS's spec.
     """
 
-    control_message = mocking.get_message(INVALID_EMPTY_RESPONSE)
+    control_message = test.util.get_message(INVALID_EMPTY_RESPONSE)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'MAPADDRESS', control_message)
 
-    control_message = mocking.get_message(INVALID_RESPONSE)
+    control_message = test.util.get_message(INVALID_RESPONSE)
     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 c95076a..d8fb762 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -12,8 +12,9 @@ import stem.util.proc
 import stem.util.system
 import stem.version
 
+import test.util
+
 from stem.response.protocolinfo import AuthMethod
-from test import mocking
 
 try:
   # added in python 3.3
@@ -70,7 +71,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     """
 
     # working case
-    control_message = mocking.get_message(NO_AUTH)
+    control_message = test.util.get_message(NO_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
 
     # now this should be a ProtocolInfoResponse (ControlMessage subclass)
@@ -86,7 +87,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     self.assertRaises(TypeError, stem.response.convert, 'PROTOCOLINFO', 'hello world')
 
     # attempt to convert a different message type
-    bw_event_control_message = mocking.get_message('650 BW 32326 2856')
+    bw_event_control_message = test.util.get_message('650 BW 32326 2856')
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'PROTOCOLINFO', bw_event_control_message)
 
   def test_no_auth(self):
@@ -94,7 +95,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response when there's no authentication.
     """
 
-    control_message = mocking.get_message(NO_AUTH)
+    control_message = test.util.get_message(NO_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
 
     self.assertEqual(1, control_message.protocol_version)
@@ -108,7 +109,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with password authentication.
     """
 
-    control_message = mocking.get_message(PASSWORD_AUTH)
+    control_message = test.util.get_message(PASSWORD_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
     self.assertEqual((AuthMethod.PASSWORD, ), control_message.auth_methods)
 
@@ -118,7 +119,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     characters.
     """
 
-    control_message = mocking.get_message(COOKIE_AUTH)
+    control_message = test.util.get_message(COOKIE_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
     self.assertEqual((AuthMethod.COOKIE, ), control_message.auth_methods)
     self.assertEqual('/tmp/my data\\"dir//control_auth_cookie', control_message.cookie_path)
@@ -128,7 +129,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with multiple authentication methods.
     """
 
-    control_message = mocking.get_message(MULTIPLE_AUTH)
+    control_message = test.util.get_message(MULTIPLE_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
     self.assertEqual((AuthMethod.COOKIE, AuthMethod.PASSWORD), control_message.auth_methods)
     self.assertEqual('/home/atagar/.tor/control_auth_cookie', control_message.cookie_path)
@@ -138,7 +139,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks a response with an unrecognized authtentication method.
     """
 
-    control_message = mocking.get_message(UNKNOWN_AUTH)
+    control_message = test.util.get_message(UNKNOWN_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
     self.assertEqual((AuthMethod.UNKNOWN, AuthMethod.PASSWORD), control_message.auth_methods)
     self.assertEqual(('MAGIC', 'PIXIE_DUST'), control_message.unknown_auth_methods)
@@ -149,7 +150,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     information to be a valid response.
     """
 
-    control_message = mocking.get_message(MINIMUM_RESPONSE)
+    control_message = test.util.get_message(MINIMUM_RESPONSE)
     stem.response.convert('PROTOCOLINFO', control_message)
 
     self.assertEqual(5, control_message.protocol_version)
@@ -164,7 +165,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     Checks an authentication cookie with a unicode path.
     """
 
-    control_message = mocking.get_message(UNICODE_COOKIE_PATH)
+    control_message = test.util.get_message(UNICODE_COOKIE_PATH)
     stem.response.convert('PROTOCOLINFO', control_message)
     self.assertEqual(EXPECTED_UNICODE_PATH, control_message.cookie_path)
 
@@ -190,7 +191,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
     with patch('stem.util.system.call') as call_mock:
       call_mock.side_effect = call_function
 
-      control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
+      control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
       stem.response.convert('PROTOCOLINFO', control_message)
 
       stem.connection._expand_cookie_path(control_message, stem.util.system.pid_by_name, 'tor')
@@ -201,6 +202,6 @@ class TestProtocolInfoResponse(unittest.TestCase):
     # leaving the path unexpanded)
 
     with patch('stem.util.system.call', Mock(return_value = None)):
-      control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
+      control_message = test.util.get_message(RELATIVE_COOKIE_PATH)
       stem.response.convert('PROTOCOLINFO', control_message)
       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 3567d59..1c05aa5 100644
--- a/test/unit/response/singleline.py
+++ b/test/unit/response/singleline.py
@@ -7,7 +7,7 @@ import unittest
 import stem.response
 import stem.socket
 
-from test import mocking
+import test.util
 
 MULTILINE_RESPONSE = """250-MULTI
 250 LINE"""
@@ -15,22 +15,22 @@ MULTILINE_RESPONSE = """250-MULTI
 
 class TestSingleLineResponse(unittest.TestCase):
   def test_single_line_response(self):
-    message = mocking.get_message('552 NOTOK')
+    message = test.util.get_message('552 NOTOK')
     stem.response.convert('SINGLELINE', message)
     self.assertEqual(False, message.is_ok())
 
-    message = mocking.get_message('250 KK')
+    message = test.util.get_message('250 KK')
     stem.response.convert('SINGLELINE', message)
     self.assertEqual(True, message.is_ok())
 
-    message = mocking.get_message('250 OK')
+    message = test.util.get_message('250 OK')
     stem.response.convert('SINGLELINE', message)
     self.assertEqual(True, message.is_ok(True))
 
-    message = mocking.get_message('250 HMM')
+    message = test.util.get_message('250 HMM')
     stem.response.convert('SINGLELINE', message)
     self.assertEqual(False, message.is_ok(True))
 
   def test_multi_line_response(self):
-    message = mocking.get_message(MULTILINE_RESPONSE)
+    message = test.util.get_message(MULTILINE_RESPONSE)
     self.assertRaises(stem.ProtocolError, stem.response.convert, 'SINGLELINE', message)
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index c979f07..2075bdf 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -15,6 +15,8 @@ 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
@@ -22,7 +24,6 @@ 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 test import mocking
 from test.unit import exec_documentation_example
 
 try:
@@ -99,7 +100,7 @@ A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB: caerSidi
 
 
 def _get_event(content):
-  controller_event = mocking.get_message(content)
+  controller_event = test.util.get_message(content)
   stem.response.convert('EVENT', controller_event)
   return controller_event
 
diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py
index 5f65212..00f2af0 100644
--- a/test/unit/util/proc.py
+++ b/test/unit/util/proc.py
@@ -6,9 +6,10 @@ import io
 import re
 import unittest
 
+import test.util
+
 from stem.util import proc
 from stem.util.connection import Connection
-from test import mocking
 
 try:
   from unittest.mock import Mock, patch
@@ -105,7 +106,7 @@ class TestProc(unittest.TestCase):
     """
 
     # list of all combinations of args with respective return values
-    stat_combinations = mocking.get_all_combinations([
+    stat_combinations = test.util.get_all_combinations([
       ('command', 'test_program'),
       ('utime', '0.13'),
       ('stime', '0.14'),
diff --git a/test/util.py b/test/util.py
index 864baab..7b6f312 100644
--- a/test/util.py
+++ b/test/util.py
@@ -18,6 +18,21 @@ Tasks are...
 
 ::
 
+  Initialization
+  |- check_stem_version - checks our version of stem
+  |- check_tor_version - checks our version of tor
+  |- check_python_version - checks our version of python
+  |- check_cryptography_version - checks our version of cryptography
+  |- check_pynacl_version - checks our version of pynacl
+  |- check_pyflakes_version - checks our version of pyflakes
+  |- check_pycodestyle_version - checks our version of pycodestyle
+  |- clean_orphaned_pyc - removes any *.pyc without a corresponding *.py
+  +- check_for_unused_tests - checks to see if any tests are missing from our settings
+
+Lastly, this module provides generally useful test helpers...
+
+::
+
   Test Requirements
   |- only_run_once - skip test if it has been ran before
   |- require - skips the test unless a requirement is met
@@ -32,20 +47,15 @@ Tasks are...
   |- require_ptrace - requires 'DisableDebuggerAttachment' to be set
   +- require_online - skips unless targets allow for online tests
 
-  Initialization
-  |- check_stem_version - checks our version of stem
-  |- check_tor_version - checks our version of tor
-  |- check_python_version - checks our version of python
-  |- check_cryptography_version - checks our version of cryptography
-  |- check_pynacl_version - checks our version of pynacl
-  |- check_pyflakes_version - checks our version of pyflakes
-  |- check_pycodestyle_version - checks our version of pycodestyle
-  |- clean_orphaned_pyc - removes any *.pyc without a corresponding *.py
-  +- check_for_unused_tests - checks to see if any tests are missing from our settings
-
+  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
   tor_version - provides the version of tor we're testing against
 """
 
+import hashlib
+import itertools
 import re
 import os
 import sys
@@ -464,6 +474,94 @@ def run_tasks(category, *tasks):
   println()
 
 
+def get_all_combinations(attr, include_empty = False):
+  """
+  Provides an iterator for all combinations of a set of attributes. For
+  instance...
+
+  ::
+
+    >>> list(test.mocking.get_all_combinations(['a', 'b', 'c']))
+    [('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]
+
+  :param list attr: attributes to provide combinations for
+  :param bool include_empty: includes an entry with zero items if True
+  :returns: iterator for all combinations
+  """
+
+  # Makes an itertools.product() call for 'i' copies of attr...
+  #
+  # * itertools.product(attr) => all one-element combinations
+  # * itertools.product(attr, attr) => all two-element combinations
+  # * ... etc
+
+  if include_empty:
+    yield ()
+
+  seen = set()
+  for index in range(1, len(attr) + 1):
+    product_arg = [attr for _ in range(index)]
+
+    for item in itertools.product(*product_arg):
+      # deduplicate, sort, and only provide if we haven't seen it yet
+      item = tuple(sorted(set(item)))
+
+      if item not in seen:
+        seen.add(item)
+        yield item
+
+
+def random_fingerprint():
+  """
+  Provides a random relay 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
+  base instance is minimal, with its version set to one and everything else
+  left with the default.
+
+  :param dict attributes: attributes to customize the response with
+
+  :returns: stem.response.protocolinfo.ProtocolInfoResponse instance
+  """
+
+  protocolinfo_response = get_message('250-PROTOCOLINFO 1\n250 OK')
+  stem.response.convert('PROTOCOLINFO', protocolinfo_response)
+
+  for attr in attributes:
+    setattr(protocolinfo_response, attr, attributes[attr])
+
+  return protocolinfo_response
+
+
 def tor_version():
   """
   Provides the version of tor we're testing against.





More information about the tor-commits mailing list