[tor-commits] [stem/master] Merge remaining test.util into base test module

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


commit 2170254997d5a3e44bc12898a0c3acb94f70f7c7
Author: Damian Johnson <atagar at 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





More information about the tor-commits mailing list