commit 2170254997d5a3e44bc12898a0c3acb94f70f7c7 Author: Damian Johnson atagar@torproject.org Date: Sun May 21 19:26:52 2017 -0700
Merge remaining test.util into base test module
Now that we got this down to a small size we can merge what remains into test/__init__.py. --- run_tests.py | 7 +- test/__init__.py | 145 ++++++++++++++++++++++++- test/arguments.py | 17 ++- test/integ/connection/authentication.py | 7 +- test/integ/control/controller.py | 19 ++-- test/integ/descriptor/extrainfo_descriptor.py | 5 +- test/integ/descriptor/microdescriptor.py | 5 +- test/integ/descriptor/networkstatus.py | 11 +- test/integ/descriptor/server_descriptor.py | 5 +- test/integ/installation.py | 14 +-- test/integ/interpreter.py | 6 +- test/integ/manual.py | 3 +- test/integ/response/protocolinfo.py | 9 +- test/integ/socket/control_socket.py | 5 +- test/require.py | 8 +- test/runner.py | 14 +-- test/settings.cfg | 3 +- test/task.py | 15 ++- test/unit/__init__.py | 4 +- test/unit/connection/authentication.py | 4 +- test/unit/doctest.py | 4 +- test/unit/installation.py | 14 +-- test/unit/util/proc.py | 4 +- test/util.py | 146 -------------------------- 24 files changed, 229 insertions(+), 245 deletions(-)
diff --git a/run_tests.py b/run_tests.py index 6beb659..725dd5a 100755 --- a/run_tests.py +++ b/run_tests.py @@ -26,15 +26,14 @@ import stem.util.system import stem.util.test_tools import stem.version
+import test import test.arguments import test.integ.installation import test.output import test.runner import test.task -import test.util
from test.output import STATUS, SUCCESS, ERROR, NO_NL, STDERR, println -from test.util import STEM_BASE
CONFIG = stem.util.conf.config_dict('test', { 'test.unit_tests': '', @@ -158,7 +157,7 @@ def main(): sys.exit(1)
test_config = stem.util.conf.get_config('test') - test_config.load(os.path.join(STEM_BASE, 'test', 'settings.cfg')) + test_config.load(os.path.join(test.STEM_BASE, 'test', 'settings.cfg'))
try: args = test.arguments.parse(sys.argv[1:]) @@ -377,7 +376,7 @@ def main():
println('TESTING PASSED %s\n' % runtime_label, SUCCESS)
- new_capabilities = test.util.get_new_capabilities() + new_capabilities = test.get_new_capabilities()
if new_capabilities: println(NEW_CAPABILITIES_FOUND, ERROR) diff --git a/test/__init__.py b/test/__init__.py index 8ea70d2..355497c 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -2,7 +2,15 @@ # See LICENSE for licensing information
""" -Unit and integration tests for the stem library. +Unit and integration tests for the stem library. Helpers include... + +:: + + get_new_capabilities - missing capabilities found while testing + register_new_capability - note that tor feature stem lacks + + get_all_combinations - provides all combinations of attributes + tor_version - provides the version of tor we're testing against """
__all__ = [ @@ -10,5 +18,138 @@ __all__ = [ 'output', 'prompt', 'runner', - 'util', ] + +import itertools +import os + +import stem.util.enum +import stem.version + +# Integration targets fall into two categories: +# +# * Run Targets (like RUN_COOKIE and RUN_PTRACE) which customize our torrc. +# We do an integration test run for each run target we get. +# +# * Attribute Target (like CHROOT and ONLINE) which indicates +# non-configuration changes to your test runs. These are applied to all +# integration runs that we perform. + +Target = stem.util.enum.UppercaseEnum( + 'ONLINE', + 'RELATIVE', + 'CHROOT', + 'RUN_NONE', + 'RUN_OPEN', + 'RUN_PASSWORD', + 'RUN_COOKIE', + 'RUN_MULTIPLE', + 'RUN_SOCKET', + 'RUN_SCOOKIE', + 'RUN_PTRACE', + 'RUN_ALL', +) + +TOR_VERSION = None + +# We make some paths relative to stem's base directory (the one above us) +# rather than the process' cwd. This doesn't end with a slash. + +STEM_BASE = os.path.sep.join(__file__.split(os.path.sep)[:-2]) + +# Store new capabilities (events, descriptor entries, etc.) + +NEW_CAPABILITIES = [] +NEW_CAPABILITIES_SUPPRESSION_TOKENS = set() + +# File extensions of contents that should be ignored. + +IGNORED_FILE_TYPES = [] + +with open(os.path.join(STEM_BASE, '.gitignore')) as ignore_file: + for line in ignore_file: + if line.startswith('*.'): + IGNORED_FILE_TYPES.append(line[2:].strip()) + + +def get_new_capabilities(): + """ + Provides a list of capabilities tor supports but stem doesn't, as discovered + while running our tests. + + :returns: **list** of (type, message) tuples for the capabilities + """ + + return NEW_CAPABILITIES + + +def register_new_capability(capability_type, msg, suppression_token = None): + """ + Register new capability found during the tests. + + :param str capability_type: type of capability this is + :param str msg: description of what we found + :param str suppression_token: skip registration if this token's already been + provided + """ + + if suppression_token not in NEW_CAPABILITIES_SUPPRESSION_TOKENS: + NEW_CAPABILITIES.append((capability_type, msg)) + + if suppression_token: + NEW_CAPABILITIES_SUPPRESSION_TOKENS.add(suppression_token) + + +def get_all_combinations(attr, include_empty = False): + """ + Provides an iterator for all combinations of a set of attributes. For + instance... + + :: + + >>> list(test.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 tor_version(tor_path = None): + """ + Provides the version of tor we're testing against. + + :param str tor_path: location of tor executable to cehck the version of + + :returns: :class:`~stem.version.Version` of tor invoked by our integration + tests + """ + + global TOR_VERSION + + if TOR_VERSION is None or tor_path: + TOR_VERSION = stem.version.get_system_tor_version(tor_path) + + return TOR_VERSION diff --git a/test/arguments.py b/test/arguments.py index 4ecd3bb..83197a7 100644 --- a/test/arguments.py +++ b/test/arguments.py @@ -10,8 +10,7 @@ import getopt
import stem.util.conf import stem.util.log - -from test.util import Target +import test
LOG_TYPE_ERROR = """\ '%s' isn't a logging runlevel, use one of the following instead: @@ -30,7 +29,7 @@ DEFAULT_ARGS = { 'specific_test': None, 'logging_runlevel': None, 'tor_path': 'tor', - 'run_targets': [Target.RUN_OPEN], + 'run_targets': [test.Target.RUN_OPEN], 'attribute_targets': [], 'quiet': False, 'verbose': False, @@ -75,7 +74,7 @@ def parse(argv): run_targets, attribute_targets = [], []
integ_targets = arg.split(',') - all_run_targets = [t for t in Target if CONFIG['target.torrc'].get(t) is not None] + all_run_targets = [t for t in test.Target if CONFIG['target.torrc'].get(t) is not None]
# validates the targets and split them into run and attribute targets
@@ -83,7 +82,7 @@ def parse(argv): raise ValueError('No targets provided')
for target in integ_targets: - if target not in Target: + if target not in test.Target: raise ValueError('Invalid integration target: %s' % target) elif target in all_run_targets: run_targets.append(target) @@ -92,8 +91,8 @@ def parse(argv):
# check if we were told to use all run targets
- if Target.RUN_ALL in attribute_targets: - attribute_targets.remove(Target.RUN_ALL) + if test.Target.RUN_ALL in attribute_targets: + attribute_targets.remove(test.Target.RUN_ALL) run_targets = all_run_targets
# if no RUN_* targets are provided then keep the default (otherwise we @@ -138,10 +137,10 @@ def get_help(): help_msg = CONFIG['msg.help']
# gets the longest target length so we can show the entries in columns - target_name_length = max(map(len, Target)) + target_name_length = max(map(len, test.Target)) description_format = '\n %%-%is - %%s' % target_name_length
- for target in Target: + for target in test.Target: help_msg += description_format % (target, CONFIG['target.description'].get(target, ''))
help_msg += '\n' diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py index f43a61d..3a2fcb0 100644 --- a/test/integ/connection/authentication.py +++ b/test/integ/connection/authentication.py @@ -9,11 +9,10 @@ import unittest import stem.connection import stem.socket import stem.version +import test import test.require import test.runner
-from test.util import tor_version - # Responses given by tor for various authentication failures. These may change # in the future and if they do then this test should be updated.
@@ -44,7 +43,7 @@ def _can_authenticate(auth_type): tor_options = runner.get_options() password_auth = test.runner.Torrc.PASSWORD in tor_options cookie_auth = test.runner.Torrc.COOKIE in tor_options - safecookie_auth = cookie_auth and tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE + safecookie_auth = cookie_auth and test.tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE
if not password_auth and not cookie_auth: # open socket, anything but safecookie will work @@ -104,7 +103,7 @@ class TestAuthenticate(unittest.TestCase): def setUp(self): self.cookie_auth_methods = [stem.connection.AuthMethod.COOKIE]
- if tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: + if test.tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: self.cookie_auth_methods.append(stem.connection.AuthMethod.SAFECOOKIE)
@test.require.controller diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py index 2125242..1e4e8e8 100644 --- a/test/integ/control/controller.py +++ b/test/integ/control/controller.py @@ -18,6 +18,7 @@ import stem.response.protocolinfo import stem.socket import stem.util.str_tools import stem.version +import test import test.network import test.require import test.runner @@ -27,8 +28,6 @@ from stem.control import EventType, Listener, State from stem.exit_policy import ExitPolicy from stem.version import Requirement
-from test.util import register_new_capability, tor_version - # Router status entry for a relay with a nickname other than 'Unnamed'. This is # used for a few tests that need to look up a relay.
@@ -47,17 +46,17 @@ class TestController(unittest.TestCase): with test.runner.get_runner().get_tor_controller() as controller: for event in controller.get_info('events/names').split(): if event not in EventType: - register_new_capability('Event', event) + test.register_new_capability('Event', event)
for signal in controller.get_info('signal/names').split(): if signal not in Signal: - register_new_capability('Signal', signal) + test.register_new_capability('Signal', signal)
# new features should simply be added to enable_feature()'s docs
for feature in controller.get_info('features/names').split(): if feature not in ('EXTENDED_EVENTS', 'VERBOSE_NAMES'): - register_new_capability('Feature', feature) + test.register_new_capability('Feature', feature)
def test_from_port(self): """ @@ -266,7 +265,7 @@ class TestController(unittest.TestCase): with runner.get_tor_controller() as controller: version = controller.get_version() self.assertTrue(isinstance(version, stem.version.Version)) - self.assertEqual(version, tor_version()) + self.assertEqual(version, test.tor_version())
@test.require.controller def test_get_exit_policy(self): @@ -342,7 +341,7 @@ class TestController(unittest.TestCase): if test.runner.Torrc.COOKIE in tor_options: auth_methods.append(stem.response.protocolinfo.AuthMethod.COOKIE)
- if tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: + if test.tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: auth_methods.append(stem.response.protocolinfo.AuthMethod.SAFECOOKIE)
if test.runner.Torrc.PASSWORD in tor_options: @@ -1139,7 +1138,7 @@ class TestController(unittest.TestCase):
runner = test.runner.get_runner()
- if tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT: + if test.tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT: self.skipTest('(requires server descriptors)') return
@@ -1169,7 +1168,7 @@ class TestController(unittest.TestCase):
runner = test.runner.get_runner()
- if tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT: + if test.tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT: self.skipTest('(requires server descriptors)') return
@@ -1229,7 +1228,7 @@ class TestController(unittest.TestCase): self.assertTrue(desc.nickname is not None)
for line in desc.get_unrecognized_lines(): - register_new_capability('Consensus Line', line) + test.register_new_capability('Consensus Line', line)
count += 1 if count > 10: diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py index 205b917..eada1d7 100644 --- a/test/integ/descriptor/extrainfo_descriptor.py +++ b/test/integ/descriptor/extrainfo_descriptor.py @@ -6,11 +6,10 @@ import os import unittest
import stem.descriptor +import test import test.require import test.runner
-from test.util import register_new_capability -
class TestExtraInfoDescriptor(unittest.TestCase): @test.require.only_run_once @@ -30,7 +29,7 @@ class TestExtraInfoDescriptor(unittest.TestCase): with open(descriptor_path, 'rb') as descriptor_file: for desc in stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0', validate = True): for line in desc.get_unrecognized_lines(): - register_new_capability('Extra-info Line', line) + test.register_new_capability('Extra-info Line', line)
if desc.dir_v2_responses_unknown: self.fail('Unrecognized statuses on dirreq-v2-resp lines: %s' % desc.dir_v2_responses_unknown) diff --git a/test/integ/descriptor/microdescriptor.py b/test/integ/descriptor/microdescriptor.py index c6fa80e..a38917a 100644 --- a/test/integ/descriptor/microdescriptor.py +++ b/test/integ/descriptor/microdescriptor.py @@ -6,11 +6,10 @@ import os import unittest
import stem.descriptor +import test import test.require import test.runner
-from test.util import register_new_capability -
class TestMicrodescriptor(unittest.TestCase): @test.require.only_run_once @@ -30,4 +29,4 @@ class TestMicrodescriptor(unittest.TestCase): with open(descriptor_path, 'rb') as descriptor_file: for desc in stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0', validate = True): for line in desc.get_unrecognized_lines(): - register_new_capability('Microdescriptor Line', line) + test.register_new_capability('Microdescriptor Line', line) diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py index 96fec77..83383fd 100644 --- a/test/integ/descriptor/networkstatus.py +++ b/test/integ/descriptor/networkstatus.py @@ -9,11 +9,10 @@ import stem import stem.descriptor import stem.descriptor.remote import stem.version +import test import test.require import test.runner
-from test.util import register_new_capability -
class TestNetworkStatus(unittest.TestCase): @test.require.only_run_once @@ -53,11 +52,11 @@ class TestNetworkStatus(unittest.TestCase):
for flag in router.flags: if flag not in stem.Flag and flag not in reported_flags: - register_new_capability('Flag', flag) + test.register_new_capability('Flag', flag) reported_flags.append(flag)
for line in router.get_unrecognized_lines(): - register_new_capability('Consensus Line', line, suppression_token = line.split()[0]) + test.register_new_capability('Consensus Line', line, suppression_token = line.split()[0])
# Sanity test that there's at least a hundred relays. If that's not the # case then this probably isn't a real, complete tor consensus. @@ -87,10 +86,10 @@ class TestNetworkStatus(unittest.TestCase):
for flag in router.flags: if flag not in stem.Flag: - register_new_capability('Flag (microdescriptor)', flag) + test.register_new_capability('Flag (microdescriptor)', flag) reported_flags.append(flag)
for line in router.get_unrecognized_lines(): - register_new_capability('Microdescriptor Consensus Line', line, suppression_token = line.split()[0]) + test.register_new_capability('Microdescriptor Consensus Line', line, suppression_token = line.split()[0])
self.assertTrue(count > 100) diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py index 2a3d897..7a234e3 100644 --- a/test/integ/descriptor/server_descriptor.py +++ b/test/integ/descriptor/server_descriptor.py @@ -6,11 +6,10 @@ import os import unittest
import stem.descriptor +import test import test.require import test.runner
-from test.util import register_new_capability -
class TestServerDescriptor(unittest.TestCase): @test.require.only_run_once @@ -36,4 +35,4 @@ class TestServerDescriptor(unittest.TestCase): self.assertEqual(None, desc.socks_port)
for line in desc.get_unrecognized_lines(): - register_new_capability('Server Descriptor Line', line) + test.register_new_capability('Server Descriptor Line', line) diff --git a/test/integ/installation.py b/test/integ/installation.py index 3f37df3..1cb9471 100644 --- a/test/integ/installation.py +++ b/test/integ/installation.py @@ -12,13 +12,13 @@ import unittest
import stem import stem.util.system +import test import test.require -import test.util
INSTALL_MISMATCH_MSG = "Running 'python setup.py sdist' doesn't match our git contents in the following way. The manifest in our setup.py may need to be updated...\n\n"
BASE_INSTALL_PATH = '/tmp/stem_test' -DIST_PATH = os.path.join(test.util.STEM_BASE, 'dist') +DIST_PATH = os.path.join(test.STEM_BASE, 'dist') SETUP_THREAD, INSTALL_FAILURE, INSTALL_PATH, SDIST_FAILURE = None, None, None, None
@@ -35,10 +35,10 @@ def setup(): original_cwd = os.getcwd()
try: - os.chdir(test.util.STEM_BASE) + os.chdir(test.STEM_BASE)
try: - os.chdir(test.util.STEM_BASE) + os.chdir(test.STEM_BASE) stem.util.system.call('%s setup.py install --prefix %s' % (sys.executable, BASE_INSTALL_PATH), timeout = 60) stem.util.system.call('%s setup.py clean --all' % sys.executable, timeout = 60) # tidy up the build directory site_packages_paths = glob.glob('%s/lib*/*/site-packages' % BASE_INSTALL_PATH) @@ -86,12 +86,12 @@ def _assert_has_all_files(path):
expected, installed = set(), set()
- for root, dirnames, filenames in os.walk(os.path.join(test.util.STEM_BASE, 'stem')): + for root, dirnames, filenames in os.walk(os.path.join(test.STEM_BASE, 'stem')): for filename in filenames: file_format = filename.split('.')[-1]
- if file_format not in test.util.IGNORED_FILE_TYPES: - expected.add(os.path.join(root, filename)[len(test.util.STEM_BASE) + 1:]) + if file_format not in test.IGNORED_FILE_TYPES: + expected.add(os.path.join(root, filename)[len(test.STEM_BASE) + 1:])
for root, dirnames, filenames in os.walk(path): for filename in filenames: diff --git a/test/integ/interpreter.py b/test/integ/interpreter.py index 42dd01a..5cef051 100644 --- a/test/integ/interpreter.py +++ b/test/integ/interpreter.py @@ -7,11 +7,11 @@ import tempfile import unittest
import stem.util.system +import test import test.require import test.runner -import test.util
-PROMPT_CMD = os.path.join(test.util.STEM_BASE, 'tor-prompt') +PROMPT_CMD = os.path.join(test.STEM_BASE, 'tor-prompt')
def _run_prompt(*args): @@ -44,7 +44,7 @@ class TestInterpreter(unittest.TestCase): '250-config-file=%s' % test.runner.get_runner().get_torrc_path(), '250 OK', '', - '250-version=%s' % test.util.tor_version(), + '250-version=%s' % test.tor_version(), '250 OK', ]
diff --git a/test/integ/manual.py b/test/integ/manual.py index 5c73914..846b1b6 100644 --- a/test/integ/manual.py +++ b/test/integ/manual.py @@ -10,6 +10,7 @@ import unittest
import stem.manual import stem.util.system +import test import test.runner
from stem.manual import Category @@ -96,7 +97,7 @@ class TestManual(unittest.TestCase):
if stem.util.system.is_windows(): self.skip_reason = '(unavailable on windows)' - elif test.runner.Target.ONLINE not in test.runner.get_runner().attribute_targets: + elif test.Target.ONLINE not in test.runner.get_runner().attribute_targets: self.skip_reason = '(requires online target)' elif not stem.util.system.is_available('a2x'): self.skip_reason = '(requires asciidoc)' diff --git a/test/integ/response/protocolinfo.py b/test/integ/response/protocolinfo.py index 1a00671..5d8c74f 100644 --- a/test/integ/response/protocolinfo.py +++ b/test/integ/response/protocolinfo.py @@ -9,12 +9,11 @@ import stem.connection import stem.socket import stem.util.system import stem.version +import test +import test.integ.util.system import test.require import test.runner
-from test.integ.util.system import filter_system_call -from test.util import tor_version - try: # added in python 3.3 from unittest.mock import Mock, patch @@ -76,7 +75,7 @@ class TestProtocolInfo(unittest.TestCase):
control_socket = stem.socket.ControlSocketFile(test.runner.CONTROL_SOCKET_PATH)
- call_replacement = filter_system_call(lookup_prefixes) + call_replacement = test.integ.util.system.filter_system_call(lookup_prefixes)
with patch('stem.util.system.call') as call_mock: call_mock.side_effect = call_replacement @@ -132,7 +131,7 @@ class TestProtocolInfo(unittest.TestCase): if test.runner.Torrc.COOKIE in tor_options: auth_methods.append(stem.response.protocolinfo.AuthMethod.COOKIE)
- if tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: + if test.tor_version() >= stem.version.Requirement.AUTH_SAFECOOKIE: auth_methods.append(stem.response.protocolinfo.AuthMethod.SAFECOOKIE)
chroot_path = runner.get_chroot() diff --git a/test/integ/socket/control_socket.py b/test/integ/socket/control_socket.py index 8e2658a..f479892 100644 --- a/test/integ/socket/control_socket.py +++ b/test/integ/socket/control_socket.py @@ -14,11 +14,10 @@ import unittest import stem.connection import stem.control import stem.socket +import test import test.require import test.runner
-from test.util import tor_version -
class TestControlSocket(unittest.TestCase): @test.require.controller @@ -69,7 +68,7 @@ class TestControlSocket(unittest.TestCase):
for _ in range(100): response = control_socket.recv() - self.assertTrue(str(response).startswith('version=%s' % tor_version())) + self.assertTrue(str(response).startswith('version=%s' % test.tor_version())) self.assertTrue(str(response).endswith('\nOK'))
@test.require.controller diff --git a/test/require.py b/test/require.py index a0735d8..774e5f4 100644 --- a/test/require.py +++ b/test/require.py @@ -24,8 +24,8 @@ run.
import stem.util.system import stem.version +import test import test.runner -import test.util
RAN_TESTS = []
@@ -72,12 +72,12 @@ def _can_ptrace(): # If we're running a tor version where ptrace is disabled and we didn't # set 'DisableDebuggerAttachment=1' then we can infer that it's disabled.
- has_option = test.util.tor_version() >= stem.version.Requirement.TORRC_DISABLE_DEBUGGER_ATTACHMENT + has_option = test.tor_version() >= stem.version.Requirement.TORRC_DISABLE_DEBUGGER_ATTACHMENT return not has_option or test.runner.Torrc.PTRACE in test.runner.get_runner().get_options()
def _is_online(): - return test.util.Target.ONLINE in test.runner.get_runner().attribute_targets + return test.Target.ONLINE in test.runner.get_runner().attribute_targets
def command(cmd): @@ -95,7 +95,7 @@ def version(req_version): :param stem.version.Version req_version: required tor version for the test """
- return needs(lambda: test.util.tor_version() >= req_version, 'requires %s' % req_version) + return needs(lambda: test.tor_version() >= req_version, 'requires %s' % req_version)
cryptography = needs(stem.prereq.is_crypto_available, 'requires cryptography') diff --git a/test/runner.py b/test/runner.py index 0f5a1d0..5686b4c 100644 --- a/test/runner.py +++ b/test/runner.py @@ -47,9 +47,9 @@ import stem.process import stem.socket import stem.util.conf import stem.util.enum +import test
from test.output import println, STATUS, ERROR, SUBSTATUS, NO_NL -from test.util import Target, STEM_BASE
CONFIG = stem.util.conf.config_dict('test', { 'integ.test_directory': './test/data', @@ -192,13 +192,13 @@ class Runner(object): config_test_dir = CONFIG['integ.test_directory']
if config_test_dir: - self._test_dir = stem.util.system.expand_path(config_test_dir, STEM_BASE) + self._test_dir = stem.util.system.expand_path(config_test_dir, test.STEM_BASE) else: self._test_dir = tempfile.mktemp('-stem-integ')
original_cwd, data_dir_path = os.getcwd(), self._test_dir
- if Target.RELATIVE in self.attribute_targets: + if test.Target.RELATIVE in self.attribute_targets: tor_cwd = os.path.dirname(self._test_dir)
if not os.path.exists(tor_cwd): @@ -222,7 +222,7 @@ class Runner(object): # strip the testing directory from recv_message responses if we're # simulating a chroot setup
- if Target.CHROOT in self.attribute_targets and not self._original_recv_message: + if test.Target.CHROOT in self.attribute_targets and not self._original_recv_message: # TODO: when we have a function for telling stem the chroot we'll # need to set that too
@@ -235,7 +235,7 @@ class Runner(object): stem.socket.recv_message = _chroot_recv_message
# revert our cwd back to normal - if Target.RELATIVE in self.attribute_targets: + if test.Target.RELATIVE in self.attribute_targets: os.chdir(original_cwd) except OSError as exc: raise exc @@ -523,7 +523,7 @@ class Runner(object): logging_path = CONFIG['integ.log']
if logging_path: - logging_path = stem.util.system.expand_path(logging_path, STEM_BASE) + logging_path = stem.util.system.expand_path(logging_path, test.STEM_BASE) println(' configuring logger (%s)... ' % logging_path, STATUS, NO_NL)
# delete the old log @@ -578,7 +578,7 @@ class Runner(object): self._tor_process = stem.process.launch_tor( tor_cmd = tor_cmd, torrc_path = os.path.join(self._test_dir, 'torrc'), - completion_percent = 100 if Target.ONLINE in self.attribute_targets else 5, + completion_percent = 100 if test.Target.ONLINE in self.attribute_targets else 5, init_msg_handler = lambda line: println(' %s' % line, SUBSTATUS), take_ownership = True, ) diff --git a/test/settings.cfg b/test/settings.cfg index 2b5fafc..b65ee37 100644 --- a/test/settings.cfg +++ b/test/settings.cfg @@ -135,7 +135,6 @@ pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.n pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.microdescriptor pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.tordnsel pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.hidden_service_descriptor -pycodestyle.ignore test/util.py => E402: import test.runner
# False positives from pyflakes. These are mappings between the path and the # issue. @@ -163,7 +162,7 @@ pyflakes.ignore stem/util/test_tools.py => 'pyflakes' imported but unused pyflakes.ignore stem/util/test_tools.py => 'pycodestyle' imported but unused pyflakes.ignore test/unit/response/events.py => 'from stem import *' used; unable to detect undefined names pyflakes.ignore test/unit/response/events.py => *may be undefined, or defined from star imports: stem -pyflakes.ignore test/util.py => undefined name 'test' +pyflakes.ignore test/__init__.py => undefined name 'test'
# Test modules we want to run. Modules are roughly ordered by the dependencies # so the lowest level tests come first. This is because a problem in say, diff --git a/test/task.py b/test/task.py index b2c8ba8..1089cba 100644 --- a/test/task.py +++ b/test/task.py @@ -32,9 +32,8 @@ import stem.util.conf import stem.util.system import stem.util.test_tools import stem.version - +import test import test.output -import test.util
from test.output import STATUS, ERROR, NO_NL, println
@@ -44,7 +43,7 @@ CONFIG = stem.util.conf.config_dict('test', { 'test.integ_tests': '', })
-SRC_PATHS = [os.path.join(test.util.STEM_BASE, path) for path in ( +SRC_PATHS = [os.path.join(test.STEM_BASE, path) for path in ( 'stem', 'test', 'run_tests.py', @@ -58,7 +57,7 @@ SRC_PATHS = [os.path.join(test.util.STEM_BASE, path) for path in (
def _check_tor_version(tor_path): - return str(test.util.tor_version(tor_path)).split()[0] + return str(test.tor_version(tor_path)).split()[0]
def _clean_orphaned_pyc(paths): @@ -98,7 +97,7 @@ def _check_for_unused_tests(paths):
if test_match: class_name = test_match.groups()[0] - module_name = py_path.replace(os.path.sep, '.')[len(test.util.STEM_BASE) + 1:-3] + '.' + class_name + module_name = py_path.replace(os.path.sep, '.')[len(test.STEM_BASE) + 1:-3] + '.' + class_name
if not (module_name in CONFIG['test.unit_tests'] or module_name in CONFIG['test.integ_tests']): unused_tests.append(module_name) @@ -109,7 +108,7 @@ def _check_for_unused_tests(paths):
def run(category, *tasks): """ - Runs a series of :class:`test.util.Task` instances. This simply prints 'done' + Runs a series of :class:`test.Task` instances. This simply prints 'done' or 'failed' for each unless we fail one that is marked as being required. If that happens then we print its error message and call sys.exit().
@@ -215,8 +214,8 @@ PYCODESTYLE_VERSION = ModuleVersion('checking pycodestyle version', ['pycodestyl CLEAN_PYC = Task('checking for orphaned .pyc files', _clean_orphaned_pyc, (SRC_PATHS,))
UNUSED_TESTS = Task('checking for unused tests', _check_for_unused_tests, [( - os.path.join(test.util.STEM_BASE, 'test', 'unit'), - os.path.join(test.util.STEM_BASE, 'test', 'integ'), + os.path.join(test.STEM_BASE, 'test', 'unit'), + os.path.join(test.STEM_BASE, 'test', 'integ'), )])
PYFLAKES_TASK = Task( diff --git a/test/unit/__init__.py b/test/unit/__init__.py index 94cd059..6cdbe98 100644 --- a/test/unit/__init__.py +++ b/test/unit/__init__.py @@ -3,7 +3,7 @@ Unit tests for the stem library. """
import os -import test.util +import test
__all__ = [ 'connection', @@ -17,7 +17,7 @@ __all__ = [
def exec_documentation_example(filename): - path = os.path.join(test.util.STEM_BASE, 'docs', '_static', 'example', filename) + path = os.path.join(test.STEM_BASE, 'docs', '_static', 'example', filename)
with open(path) as f: code = compile(f.read(), path, 'exec') diff --git a/test/unit/connection/authentication.py b/test/unit/connection/authentication.py index b50f97b..117b39d 100644 --- a/test/unit/connection/authentication.py +++ b/test/unit/connection/authentication.py @@ -12,7 +12,7 @@ various error conditions, and make sure that the right exception is raised. import unittest
import stem.connection -import test.util +import test
from stem.response import ControlMessage from stem.util import log @@ -86,7 +86,7 @@ class TestAuthenticate(unittest.TestCase): stem.connection.AuthChallengeFailed(None, None), stem.ControllerError(None))
- auth_method_combinations = test.util.get_all_combinations([ + auth_method_combinations = test.get_all_combinations([ stem.connection.AuthMethod.NONE, stem.connection.AuthMethod.PASSWORD, stem.connection.AuthMethod.COOKIE, diff --git a/test/unit/doctest.py b/test/unit/doctest.py index 1240324..fb74d92 100644 --- a/test/unit/doctest.py +++ b/test/unit/doctest.py @@ -13,7 +13,7 @@ import stem.util.connection import stem.util.str_tools import stem.util.system import stem.version -import test.util +import test
from stem.response import ControlMessage
@@ -37,7 +37,7 @@ ADD_ONION_RESPONSE = """\
class TestDocumentation(unittest.TestCase): def test_examples(self): - stem_dir = os.path.join(test.util.STEM_BASE, 'stem') + stem_dir = os.path.join(test.STEM_BASE, 'stem') is_failed = False
for path in stem.util.system.files_with_suffix(stem_dir, '.py'): diff --git a/test/unit/installation.py b/test/unit/installation.py index 73881de..15d9fe8 100644 --- a/test/unit/installation.py +++ b/test/unit/installation.py @@ -3,7 +3,7 @@ import os import re import unittest
-import test.util +import test
class TestInstallation(unittest.TestCase): @@ -12,7 +12,7 @@ class TestInstallation(unittest.TestCase):
@classmethod def setUpClass(self): - setup_path = os.path.join(test.util.STEM_BASE, 'setup.py') + setup_path = os.path.join(test.STEM_BASE, 'setup.py') self.skip_reason = None self.setup_contents = False
@@ -32,13 +32,13 @@ class TestInstallation(unittest.TestCase): # packages = ['stem', 'stem.descriptor', 'stem.util'],
modules = json.loads(re.search('packages = ([.*])', self.setup_contents).group(1).replace("'", '"')) - module_paths = dict([(m, os.path.join(test.util.STEM_BASE, m.replace('.', os.path.sep))) for m in modules]) + module_paths = dict([(m, os.path.join(test.STEM_BASE, m.replace('.', os.path.sep))) for m in modules])
for module, path in module_paths.items(): if not os.path.exists(path): self.fail("setup.py's module %s doesn't exist at %s" % (module, path))
- for entry in os.walk(os.path.join(test.util.STEM_BASE, 'stem')): + for entry in os.walk(os.path.join(test.STEM_BASE, 'stem')): directory = entry[0]
if directory.endswith('__pycache__'): @@ -72,13 +72,13 @@ class TestInstallation(unittest.TestCase):
for module, files in package_data.items(): for module_file in files: - data_files.append(os.path.join(test.util.STEM_BASE, module.replace('.', os.path.sep), module_file)) + data_files.append(os.path.join(test.STEM_BASE, module.replace('.', os.path.sep), module_file))
for path in data_files: if not os.path.exists(path): self.fail("setup.py installs a data file that doesn't exist: %s" % path)
- for entry in os.walk(os.path.join(test.util.STEM_BASE, 'stem')): + for entry in os.walk(os.path.join(test.STEM_BASE, 'stem')): directory = entry[0]
if directory.endswith('__pycache__'): @@ -88,7 +88,7 @@ class TestInstallation(unittest.TestCase): path = os.path.join(directory, filename) file_type = path.split('.')[-1]
- if file_type in (['py'] + test.util.IGNORED_FILE_TYPES): + if file_type in (['py'] + test.IGNORED_FILE_TYPES): continue elif path not in data_files: self.fail("setup.py doesn't install %s" % path) diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py index 00f2af0..b4e3aab 100644 --- a/test/unit/util/proc.py +++ b/test/unit/util/proc.py @@ -6,7 +6,7 @@ import io import re import unittest
-import test.util +import test
from stem.util import proc from stem.util.connection import Connection @@ -106,7 +106,7 @@ class TestProc(unittest.TestCase): """
# list of all combinations of args with respective return values - stat_combinations = test.util.get_all_combinations([ + stat_combinations = test.get_all_combinations([ ('command', 'test_program'), ('utime', '0.13'), ('stime', '0.14'), diff --git a/test/util.py b/test/util.py deleted file mode 100644 index 3decb89..0000000 --- a/test/util.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright 2012-2017, Damian Johnson and The Tor Project -# See LICENSE for licensing information - -""" -Helper functions for our test framework. - -:: - - get_all_combinations - provides all combinations of attributes - tor_version - provides the version of tor we're testing against -""" - -import itertools -import os - -import stem -import stem.util.enum -import stem.version - -# Integration targets fall into two categories: -# -# * Run Targets (like RUN_COOKIE and RUN_PTRACE) which customize our torrc. -# We do an integration test run for each run target we get. -# -# * Attribute Target (like CHROOT and ONLINE) which indicates -# non-configuration changes to your test runs. These are applied to all -# integration runs that we perform. - -Target = stem.util.enum.UppercaseEnum( - 'ONLINE', - 'RELATIVE', - 'CHROOT', - 'RUN_NONE', - 'RUN_OPEN', - 'RUN_PASSWORD', - 'RUN_COOKIE', - 'RUN_MULTIPLE', - 'RUN_SOCKET', - 'RUN_SCOOKIE', - 'RUN_PTRACE', - 'RUN_ALL', -) - -TOR_VERSION = None - -# We make some paths relative to stem's base directory (the one above us) -# rather than the process' cwd. This doesn't end with a slash. - -STEM_BASE = os.path.sep.join(__file__.split(os.path.sep)[:-2]) - -# Store new capabilities (events, descriptor entries, etc.) - -NEW_CAPABILITIES = [] -NEW_CAPABILITIES_SUPPRESSION_TOKENS = set() - -# File extensions of contents that should be ignored. - -IGNORED_FILE_TYPES = [] - -with open(os.path.join(STEM_BASE, '.gitignore')) as ignore_file: - for line in ignore_file: - if line.startswith('*.'): - IGNORED_FILE_TYPES.append(line[2:].strip()) - - -def get_new_capabilities(): - """ - Provides a list of capabilities tor supports but stem doesn't, as discovered - while running our tests. - - :returns: **list** of (type, message) tuples for the capabilities - """ - - return NEW_CAPABILITIES - - -def register_new_capability(capability_type, msg, suppression_token = None): - """ - Register new capability found during the tests. - - :param str capability_type: type of capability this is - :param str msg: description of what we found - :param str suppression_token: skip registration if this token's already been - provided - """ - - if suppression_token not in NEW_CAPABILITIES_SUPPRESSION_TOKENS: - NEW_CAPABILITIES.append((capability_type, msg)) - - if suppression_token: - NEW_CAPABILITIES_SUPPRESSION_TOKENS.add(suppression_token) - - -def get_all_combinations(attr, include_empty = False): - """ - Provides an iterator for all combinations of a set of attributes. For - instance... - - :: - - >>> list(test.util.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 tor_version(tor_path = None): - """ - Provides the version of tor we're testing against. - - :param str tor_path: location of tor executable to cehck the version of - - :returns: :class:`~stem.version.Version` of tor invoked by our integration - tests - """ - - global TOR_VERSION - - if TOR_VERSION is None or tor_path: - TOR_VERSION = stem.version.get_system_tor_version(tor_path) - - return TOR_VERSION
tor-commits@lists.torproject.org