Fixed #33719 -- Fixed test command crash when running in parallel.
Thanks Pēteris Caune for the report.
Regression in 3b3f38b3b0
.
This commit is contained in:
parent
981c23c0cc
commit
41c4cb253c
|
@ -419,7 +419,10 @@ def _init_worker(
|
||||||
start_method = multiprocessing.get_start_method()
|
start_method = multiprocessing.get_start_method()
|
||||||
|
|
||||||
if start_method == "spawn":
|
if start_method == "spawn":
|
||||||
process_setup(*process_setup_args)
|
if process_setup and callable(process_setup):
|
||||||
|
if process_setup_args is None:
|
||||||
|
process_setup_args = ()
|
||||||
|
process_setup(*process_setup_args)
|
||||||
setup_test_environment()
|
setup_test_environment()
|
||||||
|
|
||||||
for alias in connections:
|
for alias in connections:
|
||||||
|
@ -465,6 +468,7 @@ class ParallelTestSuite(unittest.TestSuite):
|
||||||
|
|
||||||
# In case someone wants to modify these in a subclass.
|
# In case someone wants to modify these in a subclass.
|
||||||
init_worker = _init_worker
|
init_worker = _init_worker
|
||||||
|
process_setup_args = ()
|
||||||
run_subsuite = _run_subsuite
|
run_subsuite = _run_subsuite
|
||||||
runner_class = RemoteTestRunner
|
runner_class = RemoteTestRunner
|
||||||
|
|
||||||
|
@ -477,6 +481,14 @@ class ParallelTestSuite(unittest.TestSuite):
|
||||||
self.serialized_contents = None
|
self.serialized_contents = None
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
def process_setup(self, *args):
|
||||||
|
"""
|
||||||
|
Stub method to simplify run() implementation. "self" is never actually
|
||||||
|
passed because a function implementing this method (__func__) is
|
||||||
|
always used, not the method itself.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self, result):
|
def run(self, result):
|
||||||
"""
|
"""
|
||||||
Distribute test cases across workers.
|
Distribute test cases across workers.
|
||||||
|
@ -496,11 +508,13 @@ class ParallelTestSuite(unittest.TestSuite):
|
||||||
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,
|
||||||
initializer=self.init_worker,
|
initializer=self.init_worker.__func__,
|
||||||
initargs=[
|
initargs=[
|
||||||
counter,
|
counter,
|
||||||
self.initial_settings,
|
self.initial_settings,
|
||||||
self.serialized_contents,
|
self.serialized_contents,
|
||||||
|
self.process_setup.__func__,
|
||||||
|
self.process_setup_args,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
args = [
|
args = [
|
||||||
|
|
|
@ -11,7 +11,6 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import warnings
|
import warnings
|
||||||
from functools import partial
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -26,7 +25,7 @@ else:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db import connection, connections
|
from django.db import connection, connections
|
||||||
from django.test import TestCase, TransactionTestCase
|
from django.test import TestCase, TransactionTestCase
|
||||||
from django.test.runner import _init_worker, get_max_test_processes, parallel_type
|
from django.test.runner import get_max_test_processes, parallel_type
|
||||||
from django.test.selenium import SeleniumTestCaseBase
|
from django.test.selenium import SeleniumTestCaseBase
|
||||||
from django.test.utils import NullTimeKeeper, TimeKeeper, get_runner
|
from django.test.utils import NullTimeKeeper, TimeKeeper, get_runner
|
||||||
from django.utils.deprecation import (
|
from django.utils.deprecation import (
|
||||||
|
@ -405,11 +404,8 @@ def django_tests(
|
||||||
parallel = 1
|
parallel = 1
|
||||||
|
|
||||||
TestRunner = get_runner(settings)
|
TestRunner = get_runner(settings)
|
||||||
TestRunner.parallel_test_suite.init_worker = partial(
|
TestRunner.parallel_test_suite.process_setup = setup_run_tests
|
||||||
_init_worker,
|
TestRunner.parallel_test_suite.process_setup_args = process_setup_args
|
||||||
process_setup=setup_run_tests,
|
|
||||||
process_setup_args=process_setup_args,
|
|
||||||
)
|
|
||||||
test_runner = TestRunner(
|
test_runner = TestRunner(
|
||||||
verbosity=verbosity,
|
verbosity=verbosity,
|
||||||
interactive=interactive,
|
interactive=interactive,
|
||||||
|
|
|
@ -19,6 +19,7 @@ from django.test import SimpleTestCase, TransactionTestCase, skipUnlessDBFeature
|
||||||
from django.test.runner import (
|
from django.test.runner import (
|
||||||
DiscoverRunner,
|
DiscoverRunner,
|
||||||
Shuffler,
|
Shuffler,
|
||||||
|
_init_worker,
|
||||||
reorder_test_bin,
|
reorder_test_bin,
|
||||||
reorder_tests,
|
reorder_tests,
|
||||||
shuffle_tests,
|
shuffle_tests,
|
||||||
|
@ -684,6 +685,46 @@ class NoInitializeSuiteTestRunnerTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunnerInitializerTests(SimpleTestCase):
|
||||||
|
|
||||||
|
# Raise an exception to don't actually run tests.
|
||||||
|
@mock.patch.object(
|
||||||
|
multiprocessing, "Pool", side_effect=Exception("multiprocessing.Pool()")
|
||||||
|
)
|
||||||
|
def test_no_initialize_suite_test_runner(self, mocked_pool):
|
||||||
|
class StubTestRunner(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)
|
||||||
|
|
||||||
|
runner = StubTestRunner(verbosity=0, interactive=False, parallel=2)
|
||||||
|
with self.assertRaisesMessage(Exception, "multiprocessing.Pool()"):
|
||||||
|
runner.run_tests(
|
||||||
|
[
|
||||||
|
"test_runner_apps.sample.tests_sample.TestDjangoTestCase",
|
||||||
|
"test_runner_apps.simple.tests",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# Initializer must be a function.
|
||||||
|
self.assertIs(mocked_pool.call_args.kwargs["initializer"], _init_worker)
|
||||||
|
|
||||||
|
|
||||||
class Ticket17477RegressionTests(AdminScriptTestCase):
|
class Ticket17477RegressionTests(AdminScriptTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
Loading…
Reference in New Issue