Refs #31169 -- Prevented infinite loop in parallel tests with custom test runner when using spawn.

Regression in 3b3f38b3b0.

Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
David Smith 2022-03-16 20:47:58 +00:00 committed by Mariusz Felisiak
parent 4f92cf87b0
commit ba298a32b3
2 changed files with 46 additions and 2 deletions

View File

@ -492,6 +492,7 @@ class ParallelTestSuite(unittest.TestSuite):
Even with tblib, errors may still occur for dynamically created Even with tblib, errors may still occur for dynamically created
exception classes which cannot be unpickled. exception classes which cannot be unpickled.
""" """
self.initialize_suite()
counter = multiprocessing.Value(ctypes.c_int, 0) counter = multiprocessing.Value(ctypes.c_int, 0)
pool = multiprocessing.Pool( pool = multiprocessing.Pool(
processes=self.processes, processes=self.processes,
@ -962,8 +963,6 @@ class DiscoverRunner:
def run_suite(self, suite, **kwargs): def run_suite(self, suite, **kwargs):
kwargs = self.get_test_runner_kwargs() kwargs = self.get_test_runner_kwargs()
runner = self.test_runner(**kwargs) runner = self.test_runner(**kwargs)
if hasattr(suite, "initialize_suite"):
suite.initialize_suite()
try: try:
return runner.run(suite) return runner.run(suite)
finally: finally:

View File

@ -639,6 +639,51 @@ class CustomTestRunnerOptionsCmdlineTests(AdminScriptTestCase):
self.assertNoOutput(out) 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): class Ticket17477RegressionTests(AdminScriptTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()