[tor-commits] [stem/master] Parallelize descriptor cache tests

atagar at torproject.org atagar at torproject.org
Thu Jun 8 17:17:55 UTC 2017


commit 34eaf109d6610e7fbc887c06df1a20367207c81d
Author: Damian Johnson <atagar at torproject.org>
Date:   Mon Jun 5 14:37:42 2017 -0700

    Parallelize descriptor cache tests
    
    We have a handful of lengthy tests that read descriptors from our data
    directory. Running these in the background.
---
 run_tests.py                                  | 19 ++++++++++++++
 stem/util/test_tools.py                       | 17 +++++++------
 test/integ/descriptor/extrainfo_descriptor.py | 25 +++++++++++--------
 test/integ/descriptor/microdescriptor.py      | 16 ++++++------
 test/integ/descriptor/networkstatus.py        | 36 +++++++++++++++------------
 test/integ/descriptor/server_descriptor.py    | 23 +++++++++--------
 test/integ/installation.py                    |  2 +-
 test/integ/process.py                         |  8 +++---
 test/settings.cfg                             | 10 ++++----
 9 files changed, 94 insertions(+), 62 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 961600c..0a162eb 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -28,6 +28,10 @@ import stem.version
 
 import test
 import test.arguments
+import test.integ.descriptor.extrainfo_descriptor
+import test.integ.descriptor.microdescriptor
+import test.integ.descriptor.networkstatus
+import test.integ.descriptor.server_descriptor
 import test.integ.installation
 import test.integ.process
 import test.output
@@ -37,6 +41,7 @@ import test.task
 from test.output import STATUS, SUCCESS, ERROR, NO_NL, STDERR, println
 
 CONFIG = stem.util.conf.config_dict('test', {
+  'integ.test_directory': './test/data',
   'test.unit_tests': '',
   'test.integ_tests': '',
   'target.prereq': {},
@@ -234,6 +239,20 @@ def main():
   skipped_tests = 0
 
   if args.run_integ:
+    default_test_dir = stem.util.system.expand_path(CONFIG['integ.test_directory'], test.STEM_BASE)
+
+    if not args.specific_test or 'test.integ.descriptor.extrainfo_descriptor'.startswith(args.specific_test):
+      test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor.run_tests(default_test_dir)
+
+    if not args.specific_test or 'test.integ.descriptor.microdescriptor'.startswith(args.specific_test):
+      test.integ.descriptor.microdescriptor.TestMicrodescriptor.run_tests(default_test_dir)
+
+    if not args.specific_test or 'test.integ.descriptor.networkstatus'.startswith(args.specific_test):
+      test.integ.descriptor.networkstatus.TestNetworkStatus.run_tests(default_test_dir)
+
+    if not args.specific_test or 'test.integ.descriptor.server_descriptor'.startswith(args.specific_test):
+      test.integ.descriptor.server_descriptor.TestServerDescriptor.run_tests(default_test_dir)
+
     if not args.specific_test or 'test.integ.installation'.startswith(args.specific_test):
       test.integ.installation.TestInstallation.run_tests()
 
diff --git a/stem/util/test_tools.py b/stem/util/test_tools.py
index 5a5813f..0d167d0 100644
--- a/stem/util/test_tools.py
+++ b/stem/util/test_tools.py
@@ -80,10 +80,10 @@ class AsyncTest(object):
   .. versionadded:: 1.6.0
   """
 
-  def __init__(self, test_runner, *test_runner_args):
-    def _wrapper(conn, runner, args):
+  def __init__(self, test_runner, args = None, threaded = False):
+    def _wrapper(conn, runner, test_args):
       try:
-        runner(*args) if args else runner()
+        runner(*test_args) if test_args else runner()
         conn.send(('success', None))
       except AssertionError as exc:
         conn.send(('failure', str(exc)))
@@ -92,14 +92,17 @@ class AsyncTest(object):
       finally:
         conn.close()
 
-    # method that can be mixed into TestCases
-
-    self.method = lambda test: self.result(test)
+    self.method = lambda test: self.result(test)  # method that can be mixed into TestCases
 
     self._result_type, self._result_msg = None, None
     self._result_lock = threading.RLock()
     self._results_pipe, child_pipe = multiprocessing.Pipe()
-    self._test_process = multiprocessing.Process(target = _wrapper, args = (child_pipe, test_runner, test_runner_args))
+
+    if threaded:
+      self._test_process = threading.Thread(target = _wrapper, args = (child_pipe, test_runner, args))
+    else:
+      self._test_process = multiprocessing.Process(target = _wrapper, args = (child_pipe, test_runner, args))
+
     self._test_process.start()
 
   def pid(self):
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index eada1d7..e2350ce 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -6,25 +6,28 @@ import os
 import unittest
 
 import stem.descriptor
+import stem.util.test_tools
 import test
 import test.require
-import test.runner
 
 
 class TestExtraInfoDescriptor(unittest.TestCase):
-  @test.require.only_run_once
-  def test_cached_descriptor(self):
+  @staticmethod
+  def run_tests(test_dir):
+    TestExtraInfoDescriptor.test_cached_descriptor = stem.util.test_tools.AsyncTest(TestExtraInfoDescriptor.test_cached_descriptor, args = (test_dir,), threaded = True).method
+
+  @staticmethod
+  def test_cached_descriptor(test_dir):
     """
     Parses the cached descriptor file in our data directory, checking that it
     doesn't raise any validation issues and looking for unrecognized descriptor
     additions.
     """
 
-    descriptor_path = test.runner.get_runner().get_test_dir('cached-extrainfo')
+    descriptor_path = os.path.join(test_dir, 'cached-extrainfo')
 
     if not os.path.exists(descriptor_path):
-      self.skipTest('(no cached descriptors)')
-      return
+      raise stem.util.test_tools.SkipTest('(no cached descriptors)')
 
     with open(descriptor_path, 'rb') as descriptor_file:
       for desc in stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0', validate = True):
@@ -32,12 +35,12 @@ class TestExtraInfoDescriptor(unittest.TestCase):
           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)
+          raise AssertionError('Unrecognized statuses on dirreq-v2-resp lines: %s' % desc.dir_v2_responses_unknown)
         elif desc.dir_v3_responses_unknown:
-          self.fail('Unrecognized statuses on dirreq-v3-resp lines: %s' % desc.dir_v3_responses_unknown)
+          raise AssertionError('Unrecognized statuses on dirreq-v3-resp lines: %s' % desc.dir_v3_responses_unknown)
         elif desc.dir_v2_direct_dl_unknown:
-          self.fail('Unrecognized stats on dirreq-v2-direct-dl lines: %s' % desc.dir_v2_direct_dl_unknown)
+          raise AssertionError('Unrecognized stats on dirreq-v2-direct-dl lines: %s' % desc.dir_v2_direct_dl_unknown)
         elif desc.dir_v3_direct_dl_unknown:
-          self.fail('Unrecognized stats on dirreq-v3-direct-dl lines: %s' % desc.dir_v2_direct_dl_unknown)
+          raise AssertionError('Unrecognized stats on dirreq-v3-direct-dl lines: %s' % desc.dir_v2_direct_dl_unknown)
         elif desc.dir_v2_tunneled_dl_unknown:
-          self.fail('Unrecognized stats on dirreq-v2-tunneled-dl lines: %s' % desc.dir_v2_tunneled_dl_unknown)
+          raise AssertionError('Unrecognized stats on dirreq-v2-tunneled-dl lines: %s' % desc.dir_v2_tunneled_dl_unknown)
diff --git a/test/integ/descriptor/microdescriptor.py b/test/integ/descriptor/microdescriptor.py
index a38917a..cc48fce 100644
--- a/test/integ/descriptor/microdescriptor.py
+++ b/test/integ/descriptor/microdescriptor.py
@@ -6,25 +6,27 @@ import os
 import unittest
 
 import stem.descriptor
+import stem.util.test_tools
 import test
-import test.require
-import test.runner
 
 
 class TestMicrodescriptor(unittest.TestCase):
-  @test.require.only_run_once
-  def test_cached_microdescriptors(self):
+  @staticmethod
+  def run_tests(test_dir):
+    TestMicrodescriptor.test_cached_microdescriptors = stem.util.test_tools.AsyncTest(TestMicrodescriptor.test_cached_microdescriptors, args = (test_dir,), threaded = True).method
+
+  @staticmethod
+  def test_cached_microdescriptors(test_dir):
     """
     Parses the cached microdescriptor file in our data directory, checking that
     it doesn't raise any validation issues and looking for unrecognized
     descriptor additions.
     """
 
-    descriptor_path = test.runner.get_runner().get_test_dir('cached-microdescs')
+    descriptor_path = os.path.join(test_dir, 'cached-microdescs')
 
     if not os.path.exists(descriptor_path):
-      self.skipTest('(no cached microdescriptors)')
-      return
+      raise stem.util.test_tools.SkipTest('(no cached descriptors)')
 
     with open(descriptor_path, 'rb') as descriptor_file:
       for desc in stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0', validate = True):
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
index 83383fd..46d7654 100644
--- a/test/integ/descriptor/networkstatus.py
+++ b/test/integ/descriptor/networkstatus.py
@@ -8,6 +8,7 @@ import unittest
 import stem
 import stem.descriptor
 import stem.descriptor.remote
+import stem.util.test_tools
 import stem.version
 import test
 import test.require
@@ -15,6 +16,11 @@ import test.runner
 
 
 class TestNetworkStatus(unittest.TestCase):
+  @staticmethod
+  def run_tests(test_dir):
+    TestNetworkStatus.test_cached_consensus = stem.util.test_tools.AsyncTest(TestNetworkStatus.test_cached_consensus, args = (test_dir,), threaded = True).method
+    TestNetworkStatus.test_cached_microdesc_consensus = stem.util.test_tools.AsyncTest(TestNetworkStatus.test_cached_microdesc_consensus, args = (test_dir,), threaded = True).method
+
   @test.require.only_run_once
   @test.require.online
   @test.require.cryptography
@@ -26,23 +32,21 @@ class TestNetworkStatus(unittest.TestCase):
 
     stem.descriptor.remote.get_consensus(document_handler = stem.descriptor.DocumentHandler.DOCUMENT, validate = True).run()
 
-  @test.require.only_run_once
-  def test_cached_consensus(self):
+  @staticmethod
+  def test_cached_consensus(test_dir):
     """
     Parses the cached-consensus file in our data directory.
     """
 
-    consensus_path = test.runner.get_runner().get_test_dir('cached-consensus')
+    consensus_path = os.path.join(test_dir, 'cached-consensus')
 
     if not os.path.exists(consensus_path):
-      self.skipTest('(no cached-consensus)')
-      return
+      raise stem.util.test_tools.SkipTest('(no cached-consensus)')
     elif stem.util.system.is_windows():
       # Unable to check memory usage on windows, so can't prevent hanging the
       # system if things go bad.
 
-      self.skipTest('(unavailable on windows)')
-      return
+      raise stem.util.test_tools.SkipTest('(unavailable on windows)')
 
     count, reported_flags = 0, []
 
@@ -61,22 +65,21 @@ class TestNetworkStatus(unittest.TestCase):
     # 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.
 
-    self.assertTrue(count > 100)
+    if count < 100:
+      raise AssertionError('%s only included %s relays' % (consensus_path, count))
 
-  @test.require.only_run_once
-  def test_cached_microdesc_consensus(self):
+  @staticmethod
+  def test_cached_microdesc_consensus(test_dir):
     """
     Parses the cached-microdesc-consensus file in our data directory.
     """
 
-    consensus_path = test.runner.get_runner().get_test_dir('cached-microdesc-consensus')
+    consensus_path = os.path.join(test_dir, 'cached-microdesc-consensus')
 
     if not os.path.exists(consensus_path):
-      self.skipTest('(no cached-microdesc-consensus)')
-      return
+      raise stem.util.test_tools.SkipTest('(no cached-microdesc-consensus)')
     elif stem.util.system.is_windows():
-      self.skipTest('(unavailable on windows)')
-      return
+      raise stem.util.test_tools.SkipTest('(unavailable on windows)')
 
     count, reported_flags = 0, []
 
@@ -92,4 +95,5 @@ class TestNetworkStatus(unittest.TestCase):
         for line in router.get_unrecognized_lines():
           test.register_new_capability('Microdescriptor Consensus Line', line, suppression_token = line.split()[0])
 
-    self.assertTrue(count > 100)
+    if count < 100:
+      raise AssertionError('%s only included %s relays' % (consensus_path, count))
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index 7a234e3..0726c35 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -6,33 +6,34 @@ import os
 import unittest
 
 import stem.descriptor
+import stem.util.test_tools
 import test
-import test.require
-import test.runner
 
 
 class TestServerDescriptor(unittest.TestCase):
-  @test.require.only_run_once
-  def test_cached_descriptor(self):
+  @staticmethod
+  def run_tests(test_dir):
+    TestServerDescriptor.test_cached_descriptor = stem.util.test_tools.AsyncTest(TestServerDescriptor.test_cached_descriptor, args = (test_dir,), threaded = True).method
+
+  @staticmethod
+  def test_cached_descriptor(test_dir):
     """
     Parses the cached descriptor file in our data directory, checking that it
     doesn't raise any validation issues and looking for unrecognized descriptor
     additions.
     """
 
-    descriptor_path = test.runner.get_runner().get_test_dir('cached-descriptors')
+    descriptor_path = os.path.join(test_dir, 'cached-descriptors')
 
     if not os.path.exists(descriptor_path):
-      self.skipTest('(no cached descriptors)')
-      return
+      raise stem.util.test_tools.SkipTest('(no cached descriptors)')
 
     with open(descriptor_path, 'rb') as descriptor_file:
       for desc in stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0', validate = True):
         # the following attributes should be deprecated, and not appear in the wild
-        self.assertEqual(None, desc.read_history_end)
-        self.assertEqual(None, desc.write_history_end)
-        self.assertEqual(None, desc.eventdns)
-        self.assertEqual(None, desc.socks_port)
+
+        if desc.read_history_end or desc.write_history_end or desc.eventdns or desc.socks_port:
+          raise AssertionError('deprecated attribute appeared on: %s' % desc)
 
         for line in desc.get_unrecognized_lines():
           test.register_new_capability('Server Descriptor Line', line)
diff --git a/test/integ/installation.py b/test/integ/installation.py
index 7cf5346..0253efd 100644
--- a/test/integ/installation.py
+++ b/test/integ/installation.py
@@ -58,7 +58,7 @@ class TestInstallation(unittest.TestCase):
   def run_tests():
     test_install = stem.util.test_tools.AsyncTest(TestInstallation.test_install)
     TestInstallation.test_install = test_install.method
-    TestInstallation.test_sdist = stem.util.test_tools.AsyncTest(TestInstallation.test_sdist, test_install.pid()).method
+    TestInstallation.test_sdist = stem.util.test_tools.AsyncTest(TestInstallation.test_sdist, args = (test_install.pid(),)).method
 
   @staticmethod
   def test_install():
diff --git a/test/integ/process.py b/test/integ/process.py
index 33e3927..09c51ed 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -48,10 +48,10 @@ def random_port():
 class TestProcess(unittest.TestCase):
   @staticmethod
   def run_tests(tor_cmd):
-    TestProcess.test_launch_tor_with_config_via_file = stem.util.test_tools.AsyncTest(TestProcess.test_launch_tor_with_config_via_file, tor_cmd).method
-    TestProcess.test_launch_tor_with_config_via_stdin = stem.util.test_tools.AsyncTest(TestProcess.test_launch_tor_with_config_via_stdin, tor_cmd).method
-    TestProcess.test_take_ownership_via_pid = stem.util.test_tools.AsyncTest(TestProcess.test_take_ownership_via_pid, tor_cmd).method
-    TestProcess.test_take_ownership_via_controller = stem.util.test_tools.AsyncTest(TestProcess.test_take_ownership_via_controller, tor_cmd).method
+    TestProcess.test_launch_tor_with_config_via_file = stem.util.test_tools.AsyncTest(TestProcess.test_launch_tor_with_config_via_file, args = (tor_cmd,)).method
+    TestProcess.test_launch_tor_with_config_via_stdin = stem.util.test_tools.AsyncTest(TestProcess.test_launch_tor_with_config_via_stdin, args = (tor_cmd,)).method
+    TestProcess.test_take_ownership_via_pid = stem.util.test_tools.AsyncTest(TestProcess.test_take_ownership_via_pid, args = (tor_cmd,)).method
+    TestProcess.test_take_ownership_via_controller = stem.util.test_tools.AsyncTest(TestProcess.test_take_ownership_via_controller, args = (tor_cmd,)).method
 
   def setUp(self):
     self.data_directory = tempfile.mkdtemp()
diff --git a/test/settings.cfg b/test/settings.cfg
index 4ba2f12..52bd013 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -227,11 +227,6 @@ test.integ_tests
 |test.integ.util.proc.TestProc
 |test.integ.util.system.TestSystem
 |test.integ.interpreter.TestInterpreter
-|test.integ.descriptor.remote.TestDescriptorDownloader
-|test.integ.descriptor.server_descriptor.TestServerDescriptor
-|test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor
-|test.integ.descriptor.microdescriptor.TestMicrodescriptor
-|test.integ.descriptor.networkstatus.TestNetworkStatus
 |test.integ.version.TestVersion
 |test.integ.manual.TestManual
 |test.integ.response.protocolinfo.TestProtocolInfo
@@ -241,6 +236,11 @@ test.integ_tests
 |test.integ.connection.connect.TestConnect
 |test.integ.control.base_controller.TestBaseController
 |test.integ.control.controller.TestController
+|test.integ.descriptor.remote.TestDescriptorDownloader
+|test.integ.descriptor.server_descriptor.TestServerDescriptor
+|test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor
+|test.integ.descriptor.microdescriptor.TestMicrodescriptor
+|test.integ.descriptor.networkstatus.TestNetworkStatus
 |test.integ.installation.TestInstallation
 |test.integ.process.TestProcess
 





More information about the tor-commits mailing list