commit 34eaf109d6610e7fbc887c06df1a20367207c81d Author: Damian Johnson atagar@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
tor-commits@lists.torproject.org