summaryrefslogtreecommitdiff
path: root/buildscripts
diff options
context:
space:
mode:
authorMax Hirschhorn <max.hirschhorn@mongodb.com>2017-10-11 00:11:17 -0400
committerMax Hirschhorn <max.hirschhorn@mongodb.com>2017-10-11 00:11:17 -0400
commit8b3694d704d4c472adba87e8fb0827372324c215 (patch)
treeb8cdaff1b72eaecec44d55785b2409a802510cea /buildscripts
parent9489bb5052206c73907949eaa10b0e1eb5d322a4 (diff)
downloadmongo-8b3694d704d4c472adba87e8fb0827372324c215.tar.gz
Revert "SERVER-29522 better error codes when running concurrent tests in resmoke"
This reverts commit 1e2626463b5a7c22484c4556b77da149f4ad1ef9.
Diffstat (limited to 'buildscripts')
-rw-r--r--buildscripts/resmokelib/testing/job.py2
-rw-r--r--buildscripts/resmokelib/testing/testcases/jstest.py220
2 files changed, 70 insertions, 152 deletions
diff --git a/buildscripts/resmokelib/testing/job.py b/buildscripts/resmokelib/testing/job.py
index 4507a07a0dd..4d17152b17d 100644
--- a/buildscripts/resmokelib/testing/job.py
+++ b/buildscripts/resmokelib/testing/job.py
@@ -94,7 +94,7 @@ class Job(object):
Calls the before/after test hooks and executes 'test'.
"""
- test.configure(self.fixture)
+ test.configure(self.fixture, config.NUM_CLIENTS_PER_FIXTURE)
self._run_hooks_before_tests(test)
test(self.report)
diff --git a/buildscripts/resmokelib/testing/testcases/jstest.py b/buildscripts/resmokelib/testing/testcases/jstest.py
index aeda0f71e92..c1c93925936 100644
--- a/buildscripts/resmokelib/testing/testcases/jstest.py
+++ b/buildscripts/resmokelib/testing/testcases/jstest.py
@@ -14,44 +14,54 @@ from . import interface
from ... import config
from ... import core
from ... import utils
-from ...utils import registry
-class _SingleJSTestCase(interface.TestCase):
+class JSTestCase(interface.TestCase):
"""
A jstest to execute.
"""
- REGISTERED_NAME = registry.LEAVE_UNREGISTERED
+ REGISTERED_NAME = "js_test"
+
+ # A wrapper for the thread class that lets us propagate exceptions.
+ class ExceptionThread(threading.Thread):
+ def __init__(self, my_target, my_args):
+ threading.Thread.__init__(self, target=my_target, args=my_args)
+ self.err = None
+
+ def run(self):
+ try:
+ threading.Thread.run(self)
+ except:
+ self.err = sys.exc_info()[1]
+ else:
+ self.err = None
+
+ def _get_exception(self):
+ return self.err
+
+ DEFAULT_CLIENT_NUM = 1
def __init__(self,
logger,
js_filename,
shell_executable=None,
- shell_options=None):
- """
- Initializes the _SingleJSTestCase with the JS file to run.
- """
+ shell_options=None,
+ test_kind="JSTest"):
+ """Initializes the JSTestCase with the JS file to run."""
- interface.TestCase.__init__(self, logger, "JSTest", js_filename)
+ interface.TestCase.__init__(self, logger, test_kind, js_filename)
# Command line options override the YAML configuration.
self.shell_executable = utils.default_if_none(config.MONGO_EXECUTABLE, shell_executable)
self.js_filename = js_filename
self.shell_options = utils.default_if_none(shell_options, {}).copy()
+ self.num_clients = JSTestCase.DEFAULT_CLIENT_NUM
- def configure(self, fixture, *args, **kwargs):
+ def configure(self, fixture, num_clients=DEFAULT_CLIENT_NUM, *args, **kwargs):
interface.TestCase.configure(self, fixture, *args, **kwargs)
- def configure_shell(self):
- """
- Sets up the global variables for the shell, and data/ directory for the mongod.
-
- configure_shell() only needs to be called once per test. Therefore if creating multiple
- _SingleJSTestCase instances to be run in parallel, only call configure_shell() on one of
- them.
- """
global_vars = self.shell_options.get("global_vars", {}).copy()
data_dir = self._get_data_dir(global_vars)
@@ -76,14 +86,16 @@ class _SingleJSTestCase(interface.TestCase):
global_vars["MongoRunner.mongoShellPath"] = self.shell_executable
test_data = global_vars.get("TestData", {}).copy()
- test_data["minPort"] = core.network.PortAllocator.min_test_port(self.fixture.job_num)
- test_data["maxPort"] = core.network.PortAllocator.max_test_port(self.fixture.job_num)
+ test_data["minPort"] = core.network.PortAllocator.min_test_port(fixture.job_num)
+ test_data["maxPort"] = core.network.PortAllocator.max_test_port(fixture.job_num)
global_vars["TestData"] = test_data
self.shell_options["global_vars"] = global_vars
shutil.rmtree(data_dir, ignore_errors=True)
+ self.num_clients = num_clients
+
try:
os.makedirs(data_dir)
except os.error:
@@ -118,78 +130,34 @@ class _SingleJSTestCase(interface.TestCase):
config.MONGO_RUNNER_SUBDIR)
def run_test(self):
+ threads = []
try:
- shell = self._make_process()
- self._execute(shell)
+ # Don't thread if there is only one client.
+ if self.num_clients == 1:
+ shell = self._make_process(self.logger)
+ self._execute(shell)
+ else:
+ # If there are multiple clients, make a new thread for each client.
+ for i in xrange(self.num_clients):
+ t = self.ExceptionThread(my_target=self._run_test_in_thread, my_args=[i])
+ t.start()
+ threads.append(t)
except self.failureException:
raise
except:
self.logger.exception("Encountered an error running jstest %s.", self.basename())
raise
-
- def _make_process(self):
- return core.programs.mongo_shell_program(
- self.logger,
- executable=self.shell_executable,
- filename=self.js_filename,
- connection_string=self.fixture.get_driver_connection_url(),
- **self.shell_options)
-
-
-class JSTestCase(interface.TestCase):
- """
- A wrapper for several copies of a SingleJSTest to execute.
- """
-
- REGISTERED_NAME = "js_test"
-
- class ThreadWithException(threading.Thread):
- """
- A wrapper for the thread class that lets us propagate exceptions.
- """
-
- def __init__(self, *args, **kwargs):
- threading.Thread.__init__(self, *args, **kwargs)
- self.exc_info = None
-
- def run(self):
- try:
- threading.Thread.run(self)
- except:
- self.exc_info = sys.exc_info()
-
- def __init__(self,
- logger,
- js_filename,
- shell_executable=None,
- shell_options=None,
- test_kind="JSTest"):
- """
- Initializes the JSTestCase with the JS file to run.
- """
-
- interface.TestCase.__init__(self, logger, test_kind, js_filename)
-
- self.num_clients = config.NUM_CLIENTS_PER_FIXTURE
- self.test_case_template = _SingleJSTestCase(logger, js_filename, shell_executable,
- shell_options)
-
- def configure(self, fixture, *args, **kwargs):
- interface.TestCase.configure(self, fixture, *args, **kwargs)
- self.test_case_template.configure(fixture, *args, **kwargs)
- self.test_case_template.configure_shell()
-
- def _make_process(self):
- # This function should only be called by interface.py's as_command().
- return self.test_case_template._make_process()
-
- def _get_shell_options_for_thread(self, thread_id):
- """
- Get shell_options with an initialized TestData object for given thread.
- """
-
- # We give each _SingleJSTestCase its own copy of the shell_options.
- shell_options = self.test_case_template.shell_options.copy()
+ finally:
+ for t in threads:
+ t.join()
+ for t in threads:
+ if t._get_exception() is not None:
+ raise t._get_exception()
+
+ def _make_process(self, logger=None, thread_id=0):
+ # Since _make_process() is called by each thread, we make a shallow copy of the mongo shell
+ # options to avoid modifying the shared options for the JSTestCase.
+ shell_options = self.shell_options.copy()
global_vars = shell_options["global_vars"].copy()
test_data = global_vars["TestData"].copy()
@@ -204,70 +172,20 @@ class JSTestCase(interface.TestCase):
global_vars["TestData"] = test_data
shell_options["global_vars"] = global_vars
- return shell_options
+ # If logger is none, it means that it's not running in a thread and thus logger should be
+ # set to self.logger.
+ logger = utils.default_if_none(logger, self.logger)
- def _create_test_case_for_thread(self, logger, thread_id):
- """
- Create and configure a _SingleJSTestCase to be run in a separate thread.
- """
-
- shell_options = self._get_shell_options_for_thread(thread_id)
- test_case = _SingleJSTestCase(logger,
- self.test_case_template.js_filename,
- self.test_case_template.shell_executable,
- shell_options)
-
- test_case.configure(self.fixture)
- return test_case
-
- def _run_single_copy(self):
- test_case = self._create_test_case_for_thread(self.logger, thread_id=0)
- try:
- test_case.run_test()
- # If there was an exception, it will be logged in test_case's run_test function.
- finally:
- self.return_code = test_case.return_code
-
- def _run_multiple_copies(self):
- threads = []
- test_cases = []
- try:
- # If there are multiple clients, make a new thread for each client.
- for thread_id in xrange(self.num_clients):
- logger = self.logger.new_test_thread_logger(self.test_kind, str(thread_id))
- test_case = self._create_test_case_for_thread(logger, thread_id)
- test_cases.append(test_case)
-
- thread = self.ThreadWithException(target=test_case.run_test)
- threads.append(thread)
- thread.start()
- except:
- self.logger.exception("Encountered an error starting threads for jstest %s.",
- self.basename())
- raise
- finally:
- for thread in threads:
- thread.join()
-
- # Go through each test's return code and store the first nonzero one if it exists.
- return_code = 0
- for test_case in test_cases:
- if test_case.return_code != 0:
- return_code = test_case.return_code
- break
- self.return_code = return_code
-
- for (thread_id, thread) in enumerate(threads):
- if thread.exc_info is not None:
- if not isinstance(thread.exc_info[1], self.failureException):
- self.logger.error(
- "Encountered an error inside thread %d running jstest %s.",
- thread_id, self.basename(),
- exc_info=thread.exc_info)
- raise thread.exc_info
-
- def run_test(self):
- if self.num_clients == 1:
- self._run_single_copy()
- else:
- self._run_multiple_copies()
+ return core.programs.mongo_shell_program(
+ logger,
+ executable=self.shell_executable,
+ filename=self.js_filename,
+ connection_string=self.fixture.get_driver_connection_url(),
+ **shell_options)
+
+ def _run_test_in_thread(self, thread_id):
+ # Make a logger for each thread. When this method gets called self.logger has been
+ # overridden with a TestLogger instance by the TestReport in the startTest() method.
+ logger = self.logger.new_test_thread_logger(self.test_kind, str(thread_id))
+ shell = self._make_process(logger, thread_id)
+ self._execute(shell)