summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCeesjan Luiten <ceesjan@ytec.nl>2021-05-21 16:49:01 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-06-10 07:32:15 +0200
commitcb6c19749d342c3dc0f97d89ff6887b220cf45b8 (patch)
tree911c7a6913243e29751ac0a056be54a55febb034
parented3af3ff4b3cfb72de598f1b39a1028ba3826efb (diff)
Refs #27734 -- Prevented creation of more parallel workers than TestCases.
The parallel test runner uses multiple workers to distribute the workload. These workers are assigned a worker ID using a globally incremented variable, which determines what test database to connect to. When the worker ID surpasses the test database IDs Django will crash. This reduce likelihood of crashing parallel tests because ParallelTestSuite will no longer create more workers than TestCases. It won't eliminate the problem completely though because there are other circumstances in which new workers can be created which can then be assigned an "illegal" worker ID.
-rw-r--r--django/test/runner.py27
-rw-r--r--tests/test_runner/test_discover_runner.py6
2 files changed, 17 insertions, 16 deletions
diff --git a/django/test/runner.py b/django/test/runner.py
index 8ce7ac718b..71bee37297 100644
--- a/django/test/runner.py
+++ b/django/test/runner.py
@@ -404,8 +404,8 @@ class ParallelTestSuite(unittest.TestSuite):
run_subsuite = _run_subsuite
runner_class = RemoteTestRunner
- def __init__(self, suite, processes, failfast=False, buffer=False):
- self.subsuites = partition_suite_by_case(suite)
+ def __init__(self, subsuites, processes, failfast=False, buffer=False):
+ self.subsuites = subsuites
self.processes = processes
self.failfast = failfast
self.buffer = buffer
@@ -685,22 +685,17 @@ class DiscoverRunner:
suite = self.test_suite(all_tests)
if self.parallel > 1:
- parallel_suite = self.parallel_test_suite(
- suite,
- self.parallel,
- self.failfast,
- self.buffer,
- )
-
+ subsuites = partition_suite_by_case(suite)
# Since tests are distributed across processes on a per-TestCase
# basis, there's no need for more processes than TestCases.
- parallel_units = len(parallel_suite.subsuites)
- self.parallel = min(self.parallel, parallel_units)
-
- # If there's only one TestCase, parallelization isn't needed.
- if self.parallel > 1:
- suite = parallel_suite
-
+ processes = min(self.parallel, len(subsuites))
+ if processes > 1:
+ suite = self.parallel_test_suite(
+ subsuites,
+ processes,
+ self.failfast,
+ self.buffer,
+ )
return suite
def setup_databases(self, **kwargs):
diff --git a/tests/test_runner/test_discover_runner.py b/tests/test_runner/test_discover_runner.py
index 4eff76c5fd..3f840ee049 100644
--- a/tests/test_runner/test_discover_runner.py
+++ b/tests/test_runner/test_discover_runner.py
@@ -348,6 +348,12 @@ class DiscoverRunnerTests(SimpleTestCase):
with self.assertRaisesMessage(ValueError, msg):
DiscoverRunner(pdb=True, parallel=2)
+ def test_number_of_parallel_workers(self):
+ """Number of processes doesn't exceed the number of TestCases."""
+ runner = DiscoverRunner(parallel=5, verbosity=0)
+ suite = runner.build_suite(['test_runner_apps.tagged'])
+ self.assertEqual(suite.processes, len(suite.subsuites))
+
def test_buffer_mode_test_pass(self):
runner = DiscoverRunner(buffer=True, verbose=0)
with captured_stdout() as stdout, captured_stderr() as stderr: