summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Smith <smithdc@gmail.com>2022-03-16 20:47:58 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-03-17 10:20:13 +0100
commitba298a32b30eb270ea0bf4f8fc208223d0b40bcd (patch)
treef31581a19861e6e3c83841d5f6476779b4dbd4d7
parent4f92cf87b013801810226928ddd20097f6e4fccf (diff)
downloaddjango-ba298a32b30eb270ea0bf4f8fc208223d0b40bcd.tar.gz
Refs #31169 -- Prevented infinite loop in parallel tests with custom test runner when using spawn.
Regression in 3b3f38b3b09b0f2373e51406ecb8c9c45d36aebc. Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
-rw-r--r--django/test/runner.py3
-rw-r--r--tests/test_runner/tests.py45
2 files changed, 46 insertions, 2 deletions
diff --git a/django/test/runner.py b/django/test/runner.py
index 89bb6cf1fc..fe30d2289b 100644
--- a/django/test/runner.py
+++ b/django/test/runner.py
@@ -492,6 +492,7 @@ class ParallelTestSuite(unittest.TestSuite):
Even with tblib, errors may still occur for dynamically created
exception classes which cannot be unpickled.
"""
+ self.initialize_suite()
counter = multiprocessing.Value(ctypes.c_int, 0)
pool = multiprocessing.Pool(
processes=self.processes,
@@ -962,8 +963,6 @@ class DiscoverRunner:
def run_suite(self, suite, **kwargs):
kwargs = self.get_test_runner_kwargs()
runner = self.test_runner(**kwargs)
- if hasattr(suite, "initialize_suite"):
- suite.initialize_suite()
try:
return runner.run(suite)
finally:
diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py
index c50c54f25c..c022a6fb86 100644
--- a/tests/test_runner/tests.py
+++ b/tests/test_runner/tests.py
@@ -639,6 +639,51 @@ class CustomTestRunnerOptionsCmdlineTests(AdminScriptTestCase):
self.assertNoOutput(out)
+class NoInitializeSuiteTestRunnerTests(SimpleTestCase):
+ @mock.patch.object(multiprocessing, "get_start_method", return_value="spawn")
+ @mock.patch(
+ "django.test.runner.ParallelTestSuite.initialize_suite",
+ side_effect=Exception("initialize_suite() is called."),
+ )
+ def test_no_initialize_suite_test_runner(self, *mocked_objects):
+ """
+ The test suite's initialize_suite() method must always be called when
+ using spawn. It cannot rely on a test runner implementation.
+ """
+
+ class NoInitializeSuiteTestRunner(DiscoverRunner):
+ def setup_test_environment(self, **kwargs):
+ return
+
+ def setup_databases(self, **kwargs):
+ return
+
+ def run_checks(self, databases):
+ return
+
+ def teardown_databases(self, old_config, **kwargs):
+ return
+
+ def teardown_test_environment(self, **kwargs):
+ return
+
+ def run_suite(self, suite, **kwargs):
+ kwargs = self.get_test_runner_kwargs()
+ runner = self.test_runner(**kwargs)
+ return runner.run(suite)
+
+ with self.assertRaisesMessage(Exception, "initialize_suite() is called."):
+ runner = NoInitializeSuiteTestRunner(
+ verbosity=0, interactive=False, parallel=2
+ )
+ runner.run_tests(
+ [
+ "test_runner_apps.sample.tests_sample.TestDjangoTestCase",
+ "test_runner_apps.simple.tests",
+ ]
+ )
+
+
class Ticket17477RegressionTests(AdminScriptTestCase):
def setUp(self):
super().setUp()