From 9c2d1cbf972be4ef705436ce9d95daacf6b534e0 Mon Sep 17 00:00:00 2001 From: Adam Zapletal Date: Sat, 18 Jan 2025 16:22:10 -0600 Subject: [PATCH] Fixed #36083 -- Called check command in ParallelTestSuite workers. Workers created by ParallelTestSuite were not running system checks. In general this is fine, but system checks can have side effects that the tests expect. This patch runs system checks inside of _init_worker, which is only called by ParallelTestSuite. --- django/test/runner.py | 3 +- tests/test_runner/test_parallel.py | 61 +++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/django/test/runner.py b/django/test/runner.py index 1bfeca03af7a..6a9efee0411f 100644 --- a/django/test/runner.py +++ b/django/test/runner.py @@ -439,7 +439,7 @@ def _init_worker( used_aliases=None, ): """ - Switch to databases dedicated to this worker. + Switch to databases dedicated to this worker and run system checks. This helper lives at module-level because of the multiprocessing module's requirements. @@ -463,6 +463,7 @@ def _init_worker( process_setup(*process_setup_args) django.setup() setup_test_environment(debug=debug_mode) + call_command("check", verbosity=0, databases=used_aliases) db_aliases = used_aliases if used_aliases is not None else connections for alias in db_aliases: diff --git a/tests/test_runner/test_parallel.py b/tests/test_runner/test_parallel.py index 3af0fbf2f916..67a56b4adf7f 100644 --- a/tests/test_runner/test_parallel.py +++ b/tests/test_runner/test_parallel.py @@ -1,12 +1,15 @@ +import ctypes +import multiprocessing import pickle import sys import unittest +from unittest import mock from unittest.case import TestCase from unittest.result import TestResult from unittest.suite import TestSuite, _ErrorHolder from django.test import SimpleTestCase -from django.test.runner import ParallelTestSuite, RemoteTestResult +from django.test.runner import ParallelTestSuite, RemoteTestResult, _init_worker try: import tblib.pickling_support @@ -213,6 +216,62 @@ def test_add_duration(self): self.assertEqual(result.collectedDurations, [("None", 2.3)]) +@mock.patch("django.test.runner.connections") +@mock.patch("django.test.runner.call_command") +@mock.patch("django.test.runner.setup_test_environment") +@mock.patch("django.setup") +@mock.patch("multiprocessing.get_start_method") +class InitWorkerTests(SimpleTestCase): + def test_calls_setup_functions_when_start_method_is_spawn( + self, + mock_get_start_method, + mock_setup, + mock_setup_test_environment, + mock_call_command, + _, + ): + mock_get_start_method.return_value = "spawn" + counter = multiprocessing.Value(ctypes.c_int, 0) + _init_worker(counter, initial_settings={"default": {}}, serialized_contents={}) + + mock_setup.assert_called_once() + mock_setup_test_environment.assert_called_once() + mock_call_command.assert_called_once_with( + "check", verbosity=mock.ANY, databases=mock.ANY + ) + + def test_does_not_call_setup_functions_when_start_method_is_not_spawn( + self, + mock_get_start_method, + mock_setup, + mock_setup_test_environment, + mock_call_command, + _, + ): + mock_get_start_method.return_value = "fork" + _init_worker(multiprocessing.Value(ctypes.c_int, 0)) + + mock_setup.assert_not_called() + mock_setup_test_environment.assert_not_called() + mock_call_command.assert_not_called() + + def test_sets_up_worker_connection_with_incremented_worker_id( + self, + mock_get_start_method, + mock_setup, + mock_setup_test_environment, + mock_call_command, + mock_connections, + ): + mock_get_start_method.return_value = "fork" + connection_mock = mock.MagicMock() + mock_connections.__iter__.return_value = {"default": connection_mock} + mock_connections.__getitem__.return_value = connection_mock + _init_worker(multiprocessing.Value(ctypes.c_int, 0)) + + connection_mock.creation.setup_worker_connection.assert_called_once_with(1) + + class ParallelTestSuiteTest(SimpleTestCase): @unittest.skipUnless(tblib is not None, "requires tblib to be installed") def test_handle_add_error_before_first_test(self):