This commit is contained in:
Gangadhar Yadav 2025-11-17 20:05:39 +05:30 committed by GitHub
commit 148308ec7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 97 additions and 6 deletions

View file

@ -56,8 +56,28 @@ class DatabaseCreation(BaseDatabaseCreation):
source_database_name = orig_settings_dict["NAME"] or ":memory:"
if not self.is_in_memory_db(source_database_name):
root, ext = os.path.splitext(source_database_name)
return {**orig_settings_dict, "NAME": f"{root}_{suffix}{ext}"}
if source_database_name.startswith("file:"):
if "?" in source_database_name:
base, query = source_database_name.split("?", 1)
return {
**orig_settings_dict,
"NAME": f"{base}_{suffix}?{query}",
}
else:
return {
**orig_settings_dict,
"NAME": f"{source_database_name}_{suffix}",
}
else:
root, ext = os.path.splitext(source_database_name)
clone_name = f"{root}_{suffix}{ext}"
clone_dir = os.path.dirname(clone_name)
if clone_dir:
os.makedirs(clone_dir, exist_ok=True)
return {
**orig_settings_dict,
"NAME": clone_name,
}
start_method = multiprocessing.get_start_method()
if start_method == "fork":
@ -93,6 +113,10 @@ class DatabaseCreation(BaseDatabaseCreation):
except Exception as e:
self.log("Got an error deleting the old test database: %s" % e)
sys.exit(2)
target_dir = os.path.dirname(str(target_database_name))
if target_dir:
os.makedirs(target_dir, exist_ok=True)
try:
shutil.copy(source_database_name, target_database_name)
except Exception as e:
@ -102,7 +126,15 @@ class DatabaseCreation(BaseDatabaseCreation):
# Forkserver and spawn require migrating to disk which will be
# re-opened in setup_worker_connection.
elif multiprocessing.get_start_method() in {"forkserver", "spawn"}:
ondisk_db = sqlite3.connect(target_database_name, uri=True)
target_dir = os.path.dirname(str(target_database_name))
if target_dir:
os.makedirs(target_dir, exist_ok=True)
# Open on-disk database using a proper URI.
if str(target_database_name).startswith("file:"):
ondisk_uri = str(target_database_name)
else:
ondisk_uri = f"file:{target_database_name}"
ondisk_db = sqlite3.connect(ondisk_uri, uri=True)
self.connection.connection.backup(ondisk_db)
ondisk_db.close()
@ -142,9 +174,17 @@ class DatabaseCreation(BaseDatabaseCreation):
connection_str = (
f"file:memorydb_{alias}_{_worker_id}?mode=memory&cache=shared"
)
source_db = self.connection.Database.connect(
f"file:{alias}_{_worker_id}.sqlite3?mode=ro", uri=True
)
source_name = str(settings_dict["NAME"])
if source_name.startswith("file:"):
# Ensure read-only mode is set on URI.
source_uri = (
f"{source_name}&mode=ro"
if "?" in source_name
else f"{source_name}?mode=ro"
)
else:
source_uri = f"file:{source_name}?mode=ro"
source_db = self.connection.Database.connect(source_uri, uri=True)
target_db = sqlite3.connect(connection_str, uri=True)
source_db.backup(target_db)
source_db.close()

View file

@ -0,0 +1,51 @@
import shutil
import tempfile
import unittest
from pathlib import Path
from django.db import connection, connections
from django.test import TestCase
@unittest.skipUnless(connection.vendor == "sqlite", "SQLite tests")
class SQLiteParallelCloneTests(TestCase):
"""
Tests that cloned SQLite test databases respect the original
directory when running tests in parallel.
"""
def setUp(self):
self.test_db_dir = Path(tempfile.mkdtemp()) / "db" / "default"
self.test_db_dir.mkdir(parents=True, exist_ok=True)
self.original_settings = None
self.addCleanup(
lambda: shutil.rmtree(self.test_db_dir.parent.parent, ignore_errors=True)
)
def tearDown(self):
if self.original_settings is not None:
conn = connections["default"]
conn.settings_dict["NAME"] = self.original_settings["NAME"]
conn.settings_dict["TEST"]["NAME"] = self.original_settings["TEST"]["NAME"]
def test_clone_respects_directory(self):
conn = connections["default"]
self.original_settings = {
"NAME": conn.settings_dict["NAME"],
"TEST": {"NAME": conn.settings_dict["TEST"]["NAME"]},
}
conn.close()
db_path = str(self.test_db_dir / "db.sqlite3")
conn.settings_dict["NAME"] = db_path
conn.settings_dict["TEST"]["NAME"] = str(self.test_db_dir / "test_db.sqlite3")
clone = conn.creation.get_test_db_clone_settings(1)
clone_path = Path(clone["NAME"])
self.assertIn("db", clone_path.parts)
self.assertIn("default", clone_path.parts)
self.assertTrue(clone_path.name.startswith("db_1"))