[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [stem/master] Move test requirement functions to util



commit 6ce38d57adfaef16e8c8de75676207c4dd090fb2
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date:   Tue Apr 4 10:32:24 2017 -0700

    Move test requirement functions to util
    
    Moving the require* functions to test.util and adding a few for simple prereqs.
    Planning to add some more so we can use annotations for more common
    requirements.
---
 test/integ/connection/authentication.py           |   3 +-
 test/integ/connection/connect.py                  |   2 +-
 test/integ/control/base_controller.py             |   9 +-
 test/integ/control/controller.py                  |  17 ++--
 test/integ/descriptor/extrainfo_descriptor.py     |   9 +-
 test/integ/descriptor/microdescriptor.py          |   9 +-
 test/integ/descriptor/networkstatus.py            |  15 +--
 test/integ/descriptor/remote.py                   |   9 +-
 test/integ/descriptor/server_descriptor.py        |   9 +-
 test/integ/installation.py                        |  12 ++-
 test/integ/manual.py                              |   3 +-
 test/integ/process.py                             |  10 +-
 test/integ/response/protocolinfo.py               |   3 +-
 test/integ/socket/control_message.py              |   2 +-
 test/integ/socket/control_socket.py               |   3 +-
 test/integ/util/connection.py                     |   7 +-
 test/integ/util/proc.py                           |  19 ++--
 test/integ/util/system.py                         |  95 ++++++++++---------
 test/integ/version.py                             |   7 +-
 test/runner.py                                    |  88 +----------------
 test/unit/descriptor/certificate.py               |  17 +---
 test/unit/descriptor/export.py                    |   6 +-
 test/unit/descriptor/hidden_service_descriptor.py |  11 +--
 test/unit/descriptor/reader.py                    |  12 +--
 test/unit/installation.py                         |   7 +-
 test/unit/manual.py                               |  12 +--
 test/unit/tutorial_examples.py                    |   6 +-
 test/util.py                                      | 109 ++++++++++++++++++++++
 28 files changed, 275 insertions(+), 236 deletions(-)

diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py
index 4289fac..007042f 100644
--- a/test/integ/connection/authentication.py
+++ b/test/integ/connection/authentication.py
@@ -11,8 +11,7 @@ import stem.socket
 import stem.version
 import test.runner
 
-from test.runner import require_controller
-from test.util import tor_version
+from test.util import require_controller, 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.
diff --git a/test/integ/connection/connect.py b/test/integ/connection/connect.py
index b50fdfc..d50b3af 100644
--- a/test/integ/connection/connect.py
+++ b/test/integ/connection/connect.py
@@ -13,7 +13,7 @@ except ImportError:
 import stem.connection
 import test.runner
 
-from test.runner import require_controller
+from test.util import require_controller
 
 
 class TestConnect(unittest.TestCase):
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index dc2e478..78e6e3e 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -14,7 +14,10 @@ import stem.util.system
 import test.mocking
 import test.runner
 
-from test.runner import require_controller
+from test.util import (
+  skip,
+  require_controller,
+)
 
 
 class StateObserver(object):
@@ -47,7 +50,7 @@ class TestBaseController(unittest.TestCase):
     """
 
     if stem.util.system.is_mac():
-      test.runner.skip(self, '(ticket #6235)')
+      skip(self, '(ticket #6235)')
       return
 
     with test.runner.get_runner().get_tor_socket() as control_socket:
@@ -97,7 +100,7 @@ class TestBaseController(unittest.TestCase):
     """
 
     if stem.util.system.is_mac():
-      test.runner.skip(self, '(ticket #6235)')
+      skip(self, '(ticket #6235)')
       return
 
     with test.runner.get_runner().get_tor_socket() as control_socket:
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index a0f9b99..877c40b 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -27,13 +27,14 @@ 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
-
-from test.runner import (
+from test.util import (
+  register_new_capability,
+  tor_version,
+  skip,
+  only_run_once,
   require_controller,
   require_version,
   require_online,
-  only_run_once,
 )
 
 # Router status entry for a relay with a nickname other than 'Unnamed'. This is
@@ -1125,7 +1126,7 @@ class TestController(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if not os.path.exists(runner.get_test_dir('cached-descriptors')):
-      test.runner.skip(self, '(no cached microdescriptors)')
+      skip(self, '(no cached microdescriptors)')
       return
 
     with runner.get_tor_controller() as controller:
@@ -1147,7 +1148,7 @@ class TestController(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT:
-      test.runner.skip(self, '(requires server descriptors)')
+      skip(self, '(requires server descriptors)')
       return
 
     with runner.get_tor_controller() as controller:
@@ -1177,7 +1178,7 @@ class TestController(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if tor_version() >= Requirement.MICRODESCRIPTOR_IS_DEFAULT:
-      test.runner.skip(self, '(requires server descriptors)')
+      skip(self, '(requires server descriptors)')
       return
 
     with runner.get_tor_controller() as controller:
@@ -1357,7 +1358,7 @@ class TestController(unittest.TestCase):
 
       if TEST_ROUTER_STATUS_ENTRY is None:
         # this is only likely to occure if we can't get descriptors
-        test.runner.skip(self, '(no named relays)')
+        skip(self, '(no named relays)')
         return
 
     return TEST_ROUTER_STATUS_ENTRY
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index 740ac59..2ac44fe 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -8,8 +8,11 @@ import unittest
 import stem.descriptor
 import test.runner
 
-from test.runner import only_run_once
-from test.util import register_new_capability
+from test.util import (
+  register_new_capability,
+  skip,
+  only_run_once,
+)
 
 
 class TestExtraInfoDescriptor(unittest.TestCase):
@@ -24,7 +27,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     descriptor_path = test.runner.get_runner().get_test_dir('cached-extrainfo')
 
     if not os.path.exists(descriptor_path):
-      test.runner.skip(self, '(no cached descriptors)')
+      skip(self, '(no cached descriptors)')
       return
 
     with open(descriptor_path, 'rb') as descriptor_file:
diff --git a/test/integ/descriptor/microdescriptor.py b/test/integ/descriptor/microdescriptor.py
index fad7ef8..b1796c2 100644
--- a/test/integ/descriptor/microdescriptor.py
+++ b/test/integ/descriptor/microdescriptor.py
@@ -8,8 +8,11 @@ import unittest
 import stem.descriptor
 import test.runner
 
-from test.runner import only_run_once
-from test.util import register_new_capability
+from test.util import (
+  register_new_capability,
+  skip,
+  only_run_once,
+)
 
 
 class TestMicrodescriptor(unittest.TestCase):
@@ -24,7 +27,7 @@ class TestMicrodescriptor(unittest.TestCase):
     descriptor_path = test.runner.get_runner().get_test_dir('cached-microdescs')
 
     if not os.path.exists(descriptor_path):
-      test.runner.skip(self, '(no cached microdescriptors)')
+      skip(self, '(no cached microdescriptors)')
       return
 
     with open(descriptor_path, 'rb') as descriptor_file:
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
index f4de8f5..e6bdce2 100644
--- a/test/integ/descriptor/networkstatus.py
+++ b/test/integ/descriptor/networkstatus.py
@@ -11,8 +11,11 @@ import stem.descriptor.networkstatus
 import stem.version
 import test.runner
 
-from test.runner import only_run_once
-from test.util import register_new_capability
+from test.util import (
+  register_new_capability,
+  skip,
+  only_run_once,
+)
 
 
 class TestNetworkStatus(unittest.TestCase):
@@ -25,13 +28,13 @@ class TestNetworkStatus(unittest.TestCase):
     consensus_path = test.runner.get_runner().get_test_dir('cached-consensus')
 
     if not os.path.exists(consensus_path):
-      test.runner.skip(self, '(no cached-consensus)')
+      skip(self, '(no cached-consensus)')
       return
     elif stem.util.system.is_windows():
       # Unable to check memory usage on windows, so can't prevent hanging the
       # system if things go bad.
 
-      test.runner.skip(self, '(unavailable on windows)')
+      skip(self, '(unavailable on windows)')
       return
 
     count, reported_flags = 0, []
@@ -62,10 +65,10 @@ class TestNetworkStatus(unittest.TestCase):
     consensus_path = test.runner.get_runner().get_test_dir('cached-microdesc-consensus')
 
     if not os.path.exists(consensus_path):
-      test.runner.skip(self, '(no cached-microdesc-consensus)')
+      skip(self, '(no cached-microdesc-consensus)')
       return
     elif stem.util.system.is_windows():
-      test.runner.skip(self, '(unavailable on windows)')
+      skip(self, '(unavailable on windows)')
       return
 
     count, reported_flags = 0, []
diff --git a/test/integ/descriptor/remote.py b/test/integ/descriptor/remote.py
index 889f3d8..cd18187 100644
--- a/test/integ/descriptor/remote.py
+++ b/test/integ/descriptor/remote.py
@@ -11,11 +11,10 @@ import stem.descriptor.remote
 import stem.descriptor.router_status_entry
 import stem.descriptor.server_descriptor
 
-import test.runner
-
-from test.runner import (
-  require_online,
+from test.util import (
+  skip,
   only_run_once,
+  require_online,
 )
 
 
@@ -248,7 +247,7 @@ class TestDescriptorDownloader(unittest.TestCase):
     # Don't run this test by default. Once upon a time it was fine, but tor has
     # added so many fallbacks now that this takes a looong time. :(
 
-    test.runner.skip(self, '(skipped by default)')
+    skip(self, '(skipped by default)')
     return
 
     unsuccessful = {}
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index 86e4942..302cef1 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -9,8 +9,11 @@ import stem.descriptor
 
 import test.runner
 
-from test.runner import only_run_once
-from test.util import register_new_capability
+from test.util import (
+  register_new_capability,
+  skip,
+  only_run_once,
+)
 
 
 class TestServerDescriptor(unittest.TestCase):
@@ -25,7 +28,7 @@ class TestServerDescriptor(unittest.TestCase):
     descriptor_path = test.runner.get_runner().get_test_dir('cached-descriptors')
 
     if not os.path.exists(descriptor_path):
-      test.runner.skip(self, '(no cached descriptors)')
+      skip(self, '(no cached descriptors)')
       return
 
     with open(descriptor_path, 'rb') as descriptor_file:
diff --git a/test/integ/installation.py b/test/integ/installation.py
index 2048ea3..72f2df4 100644
--- a/test/integ/installation.py
+++ b/test/integ/installation.py
@@ -9,9 +9,13 @@ import unittest
 import stem
 import stem.util.system
 
-import test.runner
 import test.util
 
+from test.util import (
+  skip,
+  only_run_once,
+)
+
 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'
@@ -105,7 +109,7 @@ def _assert_has_all_files(path):
 
 
 class TestInstallation(unittest.TestCase):
-  @test.runner.only_run_once
+  @only_run_once
   def test_install(self):
     """
     Installs with 'python setup.py install' and checks we can use what we
@@ -121,7 +125,7 @@ class TestInstallation(unittest.TestCase):
     self.assertEqual(stem.__version__, stem.util.system.call([sys.executable, '-c', "import sys;sys.path.insert(0, '%s');import stem;print(stem.__version__)" % INSTALL_PATH])[0])
     _assert_has_all_files(INSTALL_PATH)
 
-  @test.runner.only_run_once
+  @only_run_once
   def test_sdist(self):
     """
     Creates a source distribution tarball with 'python setup.py sdist' and
@@ -130,7 +134,7 @@ class TestInstallation(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('git'):
-      test.runner.skip(self, '(git unavailable)')
+      skip(self, '(git unavailable)')
       return
 
     setup().join()
diff --git a/test/integ/manual.py b/test/integ/manual.py
index e3ab3df..187cde9 100644
--- a/test/integ/manual.py
+++ b/test/integ/manual.py
@@ -13,6 +13,7 @@ import stem.util.system
 import test.runner
 
 from stem.manual import Category
+from test.util import skip
 
 EXPECTED_CATEGORIES = set([
   'NAME',
@@ -119,7 +120,7 @@ class TestManual(unittest.TestCase):
 
   def requires_downloaded_manual(self):
     if self.skip_reason:
-      test.runner.skip(self, self.skip_reason)
+      skip(self, self.skip_reason)
       return True
     elif self.download_error:
       self.fail(self.download_error)
diff --git a/test/integ/process.py b/test/integ/process.py
index 66f2018..4cc79e3 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -22,12 +22,14 @@ import stem.util.tor_tools
 import stem.version
 import test.runner
 
-from test.runner import (
+from test.util import (
+  skip,
   require_controller,
   require_version,
-  only_run_once,
 )
 
+from test.util import only_run_once
+
 try:
   # added in python 3.3
   from unittest.mock import patch, Mock
@@ -340,6 +342,7 @@ class TestProcess(unittest.TestCase):
     )
 
     control_socket = None
+
     try:
       control_socket = stem.socket.ControlPort(port = 2778)
       stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
@@ -374,6 +377,7 @@ class TestProcess(unittest.TestCase):
     )
 
     control_socket = None
+
     try:
       control_socket = stem.socket.ControlPort(port = 2778)
       stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
@@ -437,7 +441,7 @@ class TestProcess(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('sleep'):
-      test.runner.skip(self, "('sleep' command is unavailable)")
+      skip(self, "('sleep' command is unavailable)")
       return
 
     sleep_process = subprocess.Popen(['sleep', '60'])
diff --git a/test/integ/response/protocolinfo.py b/test/integ/response/protocolinfo.py
index 5c2c092..8563cf0 100644
--- a/test/integ/response/protocolinfo.py
+++ b/test/integ/response/protocolinfo.py
@@ -12,8 +12,7 @@ import stem.version
 import test.runner
 
 from test.integ.util.system import filter_system_call
-from test.runner import require_controller
-from test.util import tor_version
+from test.util import require_controller, tor_version
 
 try:
   # added in python 3.3
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index 5f6ff42..13075ca 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -10,7 +10,7 @@ import stem.version
 import test.mocking
 import test.runner
 
-from test.runner import (
+from test.util import (
   require_controller,
   require_version,
 )
diff --git a/test/integ/socket/control_socket.py b/test/integ/socket/control_socket.py
index 1a8b50a..a4da321 100644
--- a/test/integ/socket/control_socket.py
+++ b/test/integ/socket/control_socket.py
@@ -16,8 +16,7 @@ import stem.control
 import stem.socket
 import test.runner
 
-from test.runner import require_controller
-from test.util import tor_version
+from test.util import require_controller, tor_version
 
 
 class TestControlSocket(unittest.TestCase):
diff --git a/test/integ/util/connection.py b/test/integ/util/connection.py
index c6ca04a..1145169 100644
--- a/test/integ/util/connection.py
+++ b/test/integ/util/connection.py
@@ -8,6 +8,7 @@ import unittest
 import test.runner
 
 from stem.util.connection import Resolver, get_connections, system_resolvers
+from test.util import skip
 
 
 class TestConnection(unittest.TestCase):
@@ -15,13 +16,13 @@ class TestConnection(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if test.runner.Torrc.PORT not in runner.get_options():
-      test.runner.skip(self, '(no control port)')
+      skip(self, '(no control port)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment set)')
+      skip(self, '(DisableDebuggerAttachment set)')
       return
     elif resolver not in system_resolvers():
-      test.runner.skip(self, '(resolver unavailable on this platform)')
+      skip(self, '(resolver unavailable on this platform)')
       return
 
     with runner.get_tor_socket():
diff --git a/test/integ/util/proc.py b/test/integ/util/proc.py
index 79da938..4671b3d 100644
--- a/test/integ/util/proc.py
+++ b/test/integ/util/proc.py
@@ -9,6 +9,7 @@ import unittest
 import test.runner
 
 from stem.util import proc
+from test.util import skip
 
 
 class TestProc(unittest.TestCase):
@@ -18,10 +19,10 @@ class TestProc(unittest.TestCase):
     """
 
     if not proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
     elif not test.runner.get_runner().is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     runner = test.runner.get_runner()
@@ -34,7 +35,7 @@ class TestProc(unittest.TestCase):
     """
 
     if not proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
 
     tor_pid = test.runner.get_runner().get_pid()
@@ -46,7 +47,7 @@ class TestProc(unittest.TestCase):
     """
 
     if not proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
 
     tor_pid = test.runner.get_runner().get_pid()
@@ -62,7 +63,7 @@ class TestProc(unittest.TestCase):
     """
 
     if not proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
 
     tor_cmd = test.runner.get_runner().get_tor_command(True)
@@ -83,16 +84,16 @@ class TestProc(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if not proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
     elif test.runner.Torrc.PORT not in runner.get_options():
-      test.runner.skip(self, '(no control port)')
+      skip(self, '(no control port)')
       return
     elif not test.runner.get_runner().is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
     elif not os.access('/proc/net/tcp', os.R_OK) or not os.access('/proc/net/udp', os.R_OK):
-      test.runner.skip(self, '(proc lacks read permissions)')
+      skip(self, '(proc lacks read permissions)')
       return
 
     # making a controller connection so that we have something to query for
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index be04393..eee2ccb 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -12,6 +12,8 @@ import stem.util.proc
 import stem.util.system
 import test.runner
 
+from test.util import skip
+
 try:
   # added in python 3.3
   from unittest.mock import Mock, patch
@@ -67,7 +69,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('ps'):
-      test.runner.skip(self, '(ps unavailable)')
+      skip(self, '(ps unavailable)')
       return
 
     # Check to see if the command we started tor with is running. The process
@@ -85,7 +87,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
 
     tor_pid = test.runner.get_runner().get_pid()
@@ -99,10 +101,10 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('pgrep'):
-      test.runner.skip(self, '(pgrep unavailable)')
+      skip(self, '(pgrep unavailable)')
       return
 
     pgrep_prefix = stem.util.system.GET_PID_BY_NAME_PGREP % ''
@@ -122,10 +124,10 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('pidof'):
-      test.runner.skip(self, '(pidof unavailable)')
+      skip(self, '(pidof unavailable)')
       return
 
     pidof_prefix = stem.util.system.GET_PID_BY_NAME_PIDOF % ''
@@ -145,13 +147,13 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('ps'):
-      test.runner.skip(self, '(ps unavailable)')
+      skip(self, '(ps unavailable)')
       return
     elif stem.util.system.is_bsd():
-      test.runner.skip(self, '(linux only)')
+      skip(self, '(linux only)')
       return
 
     ps_prefix = stem.util.system.GET_PID_BY_NAME_PS_LINUX % ''
@@ -171,13 +173,13 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('ps'):
-      test.runner.skip(self, '(ps unavailable)')
+      skip(self, '(ps unavailable)')
       return
     elif not stem.util.system.is_bsd():
-      test.runner.skip(self, '(bsd only)')
+      skip(self, '(bsd only)')
       return
 
     ps_prefix = stem.util.system.GET_PID_BY_NAME_PS_BSD
@@ -198,13 +200,13 @@ class TestSystem(unittest.TestCase):
 
     runner = test.runner.get_runner()
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('lsof'):
-      test.runner.skip(self, '(lsof unavailable)')
+      skip(self, '(lsof unavailable)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     lsof_prefix = stem.util.system.GET_PID_BY_NAME_LSOF % ''
@@ -227,10 +229,10 @@ class TestSystem(unittest.TestCase):
     """
 
     if self._is_extra_tor_running():
-      test.runner.skip(self, '(multiple tor instances)')
+      skip(self, '(multiple tor instances)')
       return
     elif not stem.util.system.is_available('tasklist'):
-      test.runner.skip(self, '(tasklist unavailable)')
+      skip(self, '(tasklist unavailable)')
       return
 
     runner = test.runner.get_runner()
@@ -240,23 +242,24 @@ class TestSystem(unittest.TestCase):
     """
     Checks general usage of the stem.util.system.pid_by_port function.
     """
+
     runner = test.runner.get_runner()
     if stem.util.system.is_windows():
-      test.runner.skip(self, '(unavailable on windows)')
+      skip(self, '(unavailable on windows)')
       return
     elif not _has_port():
-      test.runner.skip(self, '(test instance has no port)')
+      skip(self, '(test instance has no port)')
       return
     elif stem.util.system.is_mac() or stem.util.system.is_gentoo():
-      test.runner.skip(self, '(resolvers unavailable)')
+      skip(self, '(resolvers unavailable)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
     elif not (stem.util.system.is_available('netstat') or
               stem.util.system.is_available('sockstat') or
               stem.util.system.is_available('lsof')):
-      test.runner.skip(self, '(connection resolvers unavailable)')
+      skip(self, '(connection resolvers unavailable)')
       return
 
     tor_pid, tor_port = runner.get_pid(), test.runner.CONTROL_PORT
@@ -270,19 +273,19 @@ class TestSystem(unittest.TestCase):
 
     runner = test.runner.get_runner()
     if not _has_port():
-      test.runner.skip(self, '(test instance has no port)')
+      skip(self, '(test instance has no port)')
       return
     elif not stem.util.system.is_available('netstat'):
-      test.runner.skip(self, '(netstat unavailable)')
+      skip(self, '(netstat unavailable)')
       return
     elif stem.util.system.is_bsd() or stem.util.system.is_windows():
-      test.runner.skip(self, '(linux only)')
+      skip(self, '(linux only)')
       return
     elif stem.util.system.is_gentoo():
-      test.runner.skip(self, '(unavailable on gentoo)')
+      skip(self, '(unavailable on gentoo)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     netstat_prefix = stem.util.system.GET_PID_BY_PORT_NETSTAT
@@ -302,16 +305,16 @@ class TestSystem(unittest.TestCase):
 
     runner = test.runner.get_runner()
     if not _has_port():
-      test.runner.skip(self, '(test instance has no port)')
+      skip(self, '(test instance has no port)')
       return
     elif not stem.util.system.is_available('sockstat'):
-      test.runner.skip(self, '(sockstat unavailable)')
+      skip(self, '(sockstat unavailable)')
       return
     elif not stem.util.system.is_bsd():
-      test.runner.skip(self, '(bsd only)')
+      skip(self, '(bsd only)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     sockstat_prefix = stem.util.system.GET_PID_BY_PORT_SOCKSTAT % ''
@@ -331,16 +334,16 @@ class TestSystem(unittest.TestCase):
 
     runner = test.runner.get_runner()
     if not _has_port():
-      test.runner.skip(self, '(test instance has no port)')
+      skip(self, '(test instance has no port)')
       return
     elif not stem.util.system.is_available('lsof'):
-      test.runner.skip(self, '(lsof unavailable)')
+      skip(self, '(lsof unavailable)')
       return
     elif stem.util.system.is_mac() or stem.util.system.is_gentoo():
-      test.runner.skip(self, '(resolvers unavailable)')
+      skip(self, '(resolvers unavailable)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     lsof_prefix = stem.util.system.GET_PID_BY_PORT_LSOF
@@ -384,10 +387,10 @@ class TestSystem(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if stem.util.system.is_windows():
-      test.runner.skip(self, '(unavailable on windows)')
+      skip(self, '(unavailable on windows)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     runner_pid, tor_cwd = runner.get_pid(), runner.get_tor_cwd()
@@ -402,10 +405,10 @@ class TestSystem(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if not stem.util.system.is_available('pwdx'):
-      test.runner.skip(self, '(pwdx unavailable)')
+      skip(self, '(pwdx unavailable)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     # filter the call function to only allow this command
@@ -428,10 +431,10 @@ class TestSystem(unittest.TestCase):
     runner = test.runner.get_runner()
 
     if not stem.util.system.is_available('lsof'):
-      test.runner.skip(self, '(lsof unavailable)')
+      skip(self, '(lsof unavailable)')
       return
     elif not runner.is_ptraceable():
-      test.runner.skip(self, '(DisableDebuggerAttachment is set)')
+      skip(self, '(DisableDebuggerAttachment is set)')
       return
 
     # filter the call function to only allow this command
@@ -461,7 +464,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if not stem.util.proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
 
     call_replacement = filter_system_call(['ps '])
@@ -481,7 +484,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('ps'):
-      test.runner.skip(self, '(ps unavailable)')
+      skip(self, '(ps unavailable)')
       return
 
     pid = test.runner.get_runner().get_pid()
@@ -502,7 +505,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if not stem.util.proc.is_available():
-      test.runner.skip(self, '(proc unavailable)')
+      skip(self, '(proc unavailable)')
       return
 
     call_replacement = filter_system_call(['ps '])
@@ -520,7 +523,7 @@ class TestSystem(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('ps'):
-      test.runner.skip(self, '(ps unavailable)')
+      skip(self, '(ps unavailable)')
       return
 
     pid = test.runner.get_runner().get_pid()
@@ -550,7 +553,7 @@ class TestSystem(unittest.TestCase):
     #   '/root'
 
     if getpass.getuser() == 'root':
-      test.runner.skip(self, '(running as root)')
+      skip(self, '(running as root)')
       return
 
     self.assertEqual(os.getcwd(), stem.util.system.expand_path('.'))
diff --git a/test/integ/version.py b/test/integ/version.py
index 57e469c..06837c7 100644
--- a/test/integ/version.py
+++ b/test/integ/version.py
@@ -9,7 +9,10 @@ import stem.prereq
 import stem.version
 import test.runner
 
-from test.runner import require_controller
+from test.util import (
+  skip,
+  require_controller,
+)
 
 
 class TestVersion(unittest.TestCase):
@@ -19,7 +22,7 @@ class TestVersion(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('tor'):
-      test.runner.skip(self, "(tor isn't in our path)")
+      skip(self, "(tor isn't in our path)")
       return
 
     # Since tor is in our path we should expect to be able to get the version
diff --git a/test/runner.py b/test/runner.py
index 7c98d58..a494b65 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -11,14 +11,9 @@ about the tor test instance they're running against.
   RunnerStopped - Runner doesn't have an active tor instance
   TorInaccessable - Tor can't be queried for the information
 
-  skip - skips the current test if we can
-  require_controller - skips the test unless tor provides a controller endpoint
-  require_version - skips the test unless we meet a tor version requirement
-  require_online - skips unless targets allow for online tests
-  only_run_once - skip the test if it has been ran before
   exercise_controller - basic sanity check that a controller connection can be used
-
   get_runner - Singleton for fetching our runtime context.
+
   Runner - Runtime context for our integration tests.
     |- start - prepares and starts a tor instance for our tests to run against
     |- stop - stops our tor instance and cleans up any temporary files
@@ -90,8 +85,6 @@ Torrc = stem.util.enum.Enum(
   ('PTRACE', 'DisableDebuggerAttachment 0'),
 )
 
-RAN_TESTS = []
-
 
 class RunnerStopped(Exception):
   "Raised when we try to use a Runner that doesn't have an active tor instance"
@@ -101,85 +94,6 @@ class TorInaccessable(Exception):
   'Raised when information is needed from tor but the instance we have is inaccessible'
 
 
-def skip(test_case, message):
-  """
-  Skips the test if we can. The capability for skipping tests was added in
-  python 2.7 so callers should return after this, so they report 'success' if
-  this method is unavailable.
-
-  :param unittest.TestCase test_case: test being ran
-  :param str message: message to skip the test with
-  """
-
-  if not stem.prereq._is_python_26():
-    test_case.skipTest(message)
-
-
-def require_controller(func):
-  """
-  Skips the test unless tor provides an endpoint for controllers to attach to.
-  """
-
-  def wrapped(self, *args, **kwargs):
-    if get_runner().is_accessible():
-      return func(self, *args, **kwargs)
-    else:
-      skip(self, '(no connection)')
-
-  return wrapped
-
-
-def require_version(req_version):
-  """
-  Skips the test unless we meet the required version.
-
-  :param stem.version.Version req_version: required tor version for the test
-  """
-
-  def decorator(func):
-    def wrapped(self, *args, **kwargs):
-      if tor_version() >= req_version:
-        return func(self, *args, **kwargs)
-      else:
-        skip(self, '(requires %s)' % req_version)
-
-    return wrapped
-
-  return decorator
-
-
-def require_online(func):
-  """
-  Skips the test if we weren't started with the ONLINE target, which indicates
-  that tests requiring network connectivity should run.
-  """
-
-  def wrapped(self, *args, **kwargs):
-    if Target.ONLINE in get_runner().attribute_targets:
-      return func(self, *args, **kwargs)
-    else:
-      skip(self, '(requires online target)')
-
-  return wrapped
-
-
-def only_run_once(func):
-  """
-  Skips the test if it has ran before. If it hasn't then flags it as being ran.
-  This is useful to prevent lengthy tests that are independent of integ targets
-  from being run repeatedly with ``RUN_ALL``.
-  """
-
-  def wrapped(self, *args, **kwargs):
-    if self.id() not in RAN_TESTS:
-      RAN_TESTS.append(self.id())
-      return func(self, *args, **kwargs)
-    else:
-      skip(self, '(already ran)')
-
-  return wrapped
-
-
 def exercise_controller(test_case, controller):
   """
   Checks that we can now use the socket by issuing a 'GETINFO config-file'
diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py
index 669d9ad..0cef45a 100644
--- a/test/unit/descriptor/certificate.py
+++ b/test/unit/descriptor/certificate.py
@@ -10,10 +10,10 @@ import unittest
 import stem.descriptor.certificate
 import stem.util.str_tools
 import stem.prereq
-import test.runner
 
 from stem.descriptor.certificate import ED25519_SIGNATURE_LENGTH, CertType, ExtensionType, ExtensionFlag, Ed25519Certificate, Ed25519CertificateV1, Ed25519Extension
 from test.unit.descriptor import get_resource
+from test.util import require_pynacl
 
 ED25519_CERT = """
 AQQABhtZAaW2GoBED1IjY3A6f6GNqBEl5A83fD2Za9upGke51JGqAQAgBABnprVR
@@ -147,46 +147,37 @@ class TestEd25519Certificate(unittest.TestCase):
 
     self.assert_raises(certificate(extension_data = [b'\x00\x02\x04\x07\11\12']), "Ed25519 HAS_SIGNING_KEY extension must be 32 bytes, but was 2.")
 
+  @require_pynacl
   def test_validation_with_descriptor_key(self):
     """
     Validate a descriptor signature using the ed25519 master key within the
     descriptor.
     """
 
-    if not stem.prereq._is_pynacl_available():
-      test.runner.skip(self, '(requires pynacl module)')
-      return
-
     with open(get_resource('server_descriptor_with_ed25519'), 'rb') as descriptor_file:
       desc = next(stem.descriptor.parse_file(descriptor_file, validate = False))
 
     desc.certificate.validate(desc)
 
+  @require_pynacl
   def test_validation_with_embedded_key(self):
     """
     Validate a descriptor signature using the signing key within the ed25519
     certificate.
     """
 
-    if not stem.prereq._is_pynacl_available():
-      test.runner.skip(self, '(requires pynacl module)')
-      return
-
     with open(get_resource('server_descriptor_with_ed25519'), 'rb') as descriptor_file:
       desc = next(stem.descriptor.parse_file(descriptor_file, validate = False))
 
     desc.ed25519_master_key = None
     desc.certificate.validate(desc)
 
+  @require_pynacl
   def test_validation_with_invalid_descriptor(self):
     """
     Validate a descriptor without a valid signature.
     """
 
-    if not stem.prereq._is_pynacl_available():
-      test.runner.skip(self, '(requires pynacl module)')
-      return
-
     with open(get_resource('server_descriptor_with_ed25519'), 'rb') as descriptor_file:
       desc = next(stem.descriptor.parse_file(descriptor_file, validate = False))
 
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
index 31537c5..ae7e4a0 100644
--- a/test/unit/descriptor/export.py
+++ b/test/unit/descriptor/export.py
@@ -10,9 +10,9 @@ except ImportError:
   from io import StringIO
 
 import stem.prereq
-import test.runner
 
 from stem.descriptor.export import export_csv, export_csv_file
+from test.util import skip
 
 from test.mocking import (
   get_relay_server_descriptor,
@@ -27,7 +27,7 @@ class TestExport(unittest.TestCase):
     """
 
     if stem.prereq._is_python_26():
-      test.runner.skip(self, '(header added in python 2.7)')
+      skip(self, '(header added in python 2.7)')
       return
 
     desc = get_relay_server_descriptor()
@@ -76,7 +76,7 @@ class TestExport(unittest.TestCase):
     """
 
     if stem.prereq._is_python_26():
-      test.runner.skip(self, '(header added in python 2.7)')
+      skip(self, '(header added in python 2.7)')
       return
 
     desc = get_relay_server_descriptor()
diff --git a/test/unit/descriptor/hidden_service_descriptor.py b/test/unit/descriptor/hidden_service_descriptor.py
index 74a0974..6f9fb26 100644
--- a/test/unit/descriptor/hidden_service_descriptor.py
+++ b/test/unit/descriptor/hidden_service_descriptor.py
@@ -8,10 +8,9 @@ import unittest
 import stem.descriptor
 import stem.prereq
 
-import test.runner
-
 from test.mocking import CRYPTO_BLOB, get_hidden_service_descriptor
 from test.unit.descriptor import get_resource
+from test.util import require_cryptography
 
 from stem.descriptor.hidden_service_descriptor import (
   REQUIRED_FIELDS,
@@ -267,14 +266,12 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     self.assertEqual(datetime.datetime(2014, 10, 31, 23, 0, 0), desc.published)
     self.assertEqual([2, 3], desc.protocol_versions)
 
+  @require_cryptography
   def test_with_basic_auth(self):
     """
     Parse a descriptor with introduction-points encrypted with basic auth.
     """
 
-    if not stem.prereq.is_crypto_available():
-      return test.runner.skip(self, 'requires cryptography')
-
     descriptor_file = open(get_resource('hidden_service_basic_auth'), 'rb')
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True))
@@ -316,14 +313,12 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     self.assertTrue('MIGJAoGBAM7B/cymp' in point.service_key)
     self.assertEqual([], point.intro_authentication)
 
+  @require_cryptography
   def test_with_stealth_auth(self):
     """
     Parse a descriptor with introduction-points encrypted with stealth auth.
     """
 
-    if not stem.prereq.is_crypto_available():
-      return test.runner.skip(self, 'requires cryptography')
-
     descriptor_file = open(get_resource('hidden_service_stealth_auth'), 'rb')
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True))
diff --git a/test/unit/descriptor/reader.py b/test/unit/descriptor/reader.py
index 51043e2..ac7fd83 100644
--- a/test/unit/descriptor/reader.py
+++ b/test/unit/descriptor/reader.py
@@ -14,10 +14,10 @@ import time
 import unittest
 
 import stem.descriptor.reader
-import test.runner
 import test.unit.descriptor
 
 from stem.util import str_type, system
+from test.util import skip
 
 try:
   # added in python 3.3
@@ -191,7 +191,7 @@ class TestDescriptorReader(unittest.TestCase):
     # test relies on being unable to read a file
 
     if getpass.getuser() == 'root':
-      test.runner.skip(self, '(running as root)')
+      skip(self, '(running as root)')
       return
 
     # Skip the test on windows, since you can only set the file's
@@ -199,7 +199,7 @@ class TestDescriptorReader(unittest.TestCase):
     # http://docs.python.org/library/os.html#os.chmod
 
     if system.is_windows():
-      test.runner.skip(self, '(chmod not functional)')
+      skip(self, '(chmod not functional)')
 
     test_listing_path = self._make_processed_files_listing(BASIC_LISTING)
     os.chmod(test_listing_path, 0o077)  # remove read permissions
@@ -413,7 +413,7 @@ class TestDescriptorReader(unittest.TestCase):
     # Skip on windows since SIGALRM is unavailable
 
     if system.is_windows():
-      test.runner.skip(self, '(SIGALRM unavailable)')
+      skip(self, '(SIGALRM unavailable)')
 
     is_test_running = True
     reader = stem.descriptor.reader.DescriptorReader('/usr')
@@ -550,10 +550,10 @@ class TestDescriptorReader(unittest.TestCase):
     # test relies on being unable to read a file
 
     if getpass.getuser() == 'root':
-      test.runner.skip(self, '(running as root)')
+      skip(self, '(running as root)')
       return
     elif system.is_windows():
-      test.runner.skip(self, '(chmod not functional)')
+      skip(self, '(chmod not functional)')
       return
 
     test_path = os.path.join(self.temp_directory, 'secret_file')
diff --git a/test/unit/installation.py b/test/unit/installation.py
index 773e0f1..83d560a 100644
--- a/test/unit/installation.py
+++ b/test/unit/installation.py
@@ -3,9 +3,10 @@ import os
 import re
 import unittest
 
-import test.runner
 import test.util
 
+from test.util import skip
+
 
 class TestInstallation(unittest.TestCase):
   # TODO: remove when dropping support for python 2.6
@@ -25,7 +26,7 @@ class TestInstallation(unittest.TestCase):
 
   def test_installs_all_modules(self):
     if self.skip_reason:
-      test.runner.skip(self, self.skip_reason)
+      skip(self, self.skip_reason)
       return True
 
     # Modules cited my our setup.py looks like...
@@ -49,7 +50,7 @@ class TestInstallation(unittest.TestCase):
 
   def test_installs_all_data_files(self):
     if self.skip_reason:
-      test.runner.skip(self, self.skip_reason)
+      skip(self, self.skip_reason)
       return True
 
     # Checking that we have all non-source files. Data looks like...
diff --git a/test/unit/manual.py b/test/unit/manual.py
index becefd5..deded2d 100644
--- a/test/unit/manual.py
+++ b/test/unit/manual.py
@@ -12,7 +12,7 @@ import stem.prereq
 import stem.manual
 import stem.util.system
 
-import test.runner
+from test.util import skip
 
 try:
   # account for urllib's change between python 2.x and 3.x
@@ -160,10 +160,10 @@ class TestManual(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('man'):
-      test.runner.skip(self, '(require man command)')
+      skip(self, '(require man command)')
       return
     elif stem.util.system.is_mac():
-      test.runner.skip(self, '(man lacks --encoding arg on OSX, #18660)')
+      skip(self, '(man lacks --encoding arg on OSX, #18660)')
       return
 
     manual = stem.manual.Manual.from_man(EXAMPLE_MAN_PATH)
@@ -183,10 +183,10 @@ class TestManual(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('man'):
-      test.runner.skip(self, '(require man command)')
+      skip(self, '(require man command)')
       return
     elif stem.util.system.is_mac():
-      test.runner.skip(self, '(man lacks --encoding arg on OSX, #18660)')
+      skip(self, '(man lacks --encoding arg on OSX, #18660)')
       return
 
     manual = stem.manual.Manual.from_man(UNKNOWN_OPTIONS_MAN_PATH)
@@ -213,7 +213,7 @@ class TestManual(unittest.TestCase):
     """
 
     if not stem.util.system.is_available('man'):
-      test.runner.skip(self, '(require man command)')
+      skip(self, '(require man command)')
       return
 
     manual = stem.manual.Manual.from_man(EXAMPLE_MAN_PATH)
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index e316766..bf313ce 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -15,14 +15,14 @@ import stem.response
 import stem.descriptor.remote
 import stem.prereq
 
-import test.runner
-
 from stem.control import Controller
 from stem.util import str_type
 from stem.descriptor.remote import DIRECTORY_AUTHORITIES
 
 from test import mocking
 from test.unit import exec_documentation_example
+from test.util import skip
+
 from test.mocking import (
   get_relay_server_descriptor,
   get_router_status_entry_v3,
@@ -247,7 +247,7 @@ class TestTutorialExamples(unittest.TestCase):
       # example imports OrderedDict from collections which doesn't work under
       # python 2.6
 
-      test.runner.skip(self, "(example doesn't support python 2.6)")
+      skip(self, "(example doesn't support python 2.6)")
       return
 
     get_authorities_mock().items.return_value = [('moria1', DIRECTORY_AUTHORITIES['moria1']), ('maatuska', DIRECTORY_AUTHORITIES['maatuska'])]
diff --git a/test/util.py b/test/util.py
index 2a58e2f..75ea50e 100644
--- a/test/util.py
+++ b/test/util.py
@@ -18,6 +18,18 @@ Tasks are...
 
 ::
 
+  Test Skipping
+  |- skip - skips the current test if we can
+  |- only_run_once - skip test if it has been ran before
+  |- require - skips the test unless a requirement is met
+  |
+  |- require_cryptography - skips test unless the cryptography module is present
+  |- require_pynacl - skips test unless the pynacl module is present
+  |
+  |- require_controller - skips test unless tor provides a controller endpoint
+  |- require_version - skips test unless we meet a tor version requirement
+  +- 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
@@ -28,6 +40,8 @@ Tasks are...
   |- 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
+
+  tor_version - provides the version of tor we're testing against
 """
 
 import re
@@ -79,6 +93,7 @@ Target = stem.util.enum.UppercaseEnum(
 )
 
 TOR_VERSION = None
+RAN_TESTS = []
 
 # 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.
@@ -205,6 +220,97 @@ def get_new_capabilities():
   return NEW_CAPABILITIES
 
 
+def skip(test_case, message):
+  """
+  Skips the test if we can. The capability for skipping tests was added in
+  python 2.7 so callers should return after this, so they report 'success' if
+  this method is unavailable.
+
+  :param unittest.TestCase test_case: test being ran
+  :param str message: message to skip the test with
+  """
+
+  if not stem.prereq._is_python_26():
+    test_case.skipTest(message)
+
+
+def only_run_once(func):
+  """
+  Skips the test if it has ran before. If it hasn't then flags it as being ran.
+  This is useful to prevent lengthy tests that are independent of integ targets
+  from being run repeatedly with ``RUN_ALL``.
+  """
+
+  def wrapped(self, *args, **kwargs):
+    if self.id() not in RAN_TESTS:
+      RAN_TESTS.append(self.id())
+      return func(self, *args, **kwargs)
+    else:
+      skip(self, '(already ran)')
+
+  return wrapped
+
+
+def require(condition, message):
+  """
+  Skips teh test unless the conditional evaluates to 'true'.
+  """
+
+  def decorator(func):
+    def wrapped(self, *args, **kwargs):
+      if condition():
+        return func(self, *args, **kwargs)
+      else:
+        skip(self, '(%s)' % message)
+
+    return wrapped
+
+  return decorator
+
+
+require_cryptography = require(stem.prereq.is_crypto_available, 'requires cryptography')
+require_pynacl = require(stem.prereq._is_pynacl_available, 'requires pynacl module')
+
+
+def require_controller(func):
+  """
+  Skips the test unless tor provides an endpoint for controllers to attach to.
+  """
+
+  def wrapped(self, *args, **kwargs):
+    if test.runner.get_runner().is_accessible():
+      return func(self, *args, **kwargs)
+    else:
+      skip(self, '(no connection)')
+
+  return wrapped
+
+
+def require_version(req_version):
+  """
+  Skips the test unless we meet the required version.
+
+  :param stem.version.Version req_version: required tor version for the test
+  """
+
+  return require(lambda: tor_version() >= req_version, 'requires %s' % req_version)
+
+
+def require_online(func):
+  """
+  Skips the test if we weren't started with the ONLINE target, which indicates
+  that tests requiring network connectivity should run.
+  """
+
+  def wrapped(self, *args, **kwargs):
+    if Target.ONLINE in test.runner.get_runner().attribute_targets:
+      return func(self, *args, **kwargs)
+    else:
+      skip(self, '(requires online target)')
+
+  return wrapped
+
+
 def check_stem_version():
   return stem.__version__
 
@@ -432,3 +538,6 @@ class Task(object):
 
       println(output_msg, ERROR)
       self.error = exc
+
+
+import test.runner  # needs to be imported at the end to avoid a circular dependency



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits