commit b164478ea9c0275bc668cef32e84f4026c05a246 Author: Damian Johnson atagar@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.