Skip to content

Commit 0258e25

Browse files
committed
Rename test databases when running parallel Tox
When tests are executed using Tox in parallel, modify the test database names, to avoid name collisions between processes. This change renames the existing `django_db_modify_db_settings_xdist_suffix` fixture, to a generic `django_db_modify_db_settings_parallel_suffix` one, in case more scenarios/tools have to be considered in the future. It also handles projects where both `pytest-xdist` and parallel `tox` are being using, generating database names like `test_default_py37-django21_gw0`. Resolves #678.
1 parent c1bdb8d commit 0258e25

File tree

5 files changed

+194
-23
lines changed

5 files changed

+194
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ _build
99
.env
1010
/.coverage.*
1111
/.coverage
12+
/coverage.xml
1213
/htmlcov/
1314
.cache
1415
.pytest_cache/

docs/database.rst

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,21 +191,43 @@ If you need to customize the location of your test database, this is the
191191
fixture you want to override.
192192

193193
The default implementation of this fixture requests the
194-
:fixture:`django_db_modify_db_settings_xdist_suffix` to provide compatibility
194+
:fixture:`django_db_modify_db_settings_parallel_suffix` to provide compatibility
195195
with pytest-xdist.
196196

197197
This fixture is by default requested from :fixture:`django_db_setup`.
198198

199+
django_db_modify_db_settings_parallel_suffix
200+
""""""""""""""""""""""""""""""""""""""""""""
201+
202+
.. fixture:: django_db_modify_db_settings_parallel_suffix
203+
204+
Requesting this fixture will add a suffix to the database name when the tests
205+
are run via `pytest-xdist`, or via `tox` in parallel mode.
206+
207+
This fixture is by default requested from
208+
:fixture:`django_db_modify_db_settings`.
209+
210+
django_db_modify_db_settings_tox_suffix
211+
"""""""""""""""""""""""""""""""""""""""
212+
213+
.. fixture:: django_db_modify_db_settings_tox_suffix
214+
215+
Requesting this fixture will add a suffix to the database name when the tests
216+
are run via `tox` in parallel mode.
217+
218+
This fixture is by default requested from
219+
:fixture:`django_db_modify_db_settings_parallel_suffix`.
220+
199221
django_db_modify_db_settings_xdist_suffix
200222
"""""""""""""""""""""""""""""""""""""""""
201223

202224
.. fixture:: django_db_modify_db_settings_xdist_suffix
203225

204226
Requesting this fixture will add a suffix to the database name when the tests
205-
are run via pytest-xdist.
227+
are run via `pytest-xdist`.
206228

207229
This fixture is by default requested from
208-
:fixture:`django_db_modify_db_settings`.
230+
:fixture:`django_db_modify_db_settings_parallel_suffix`.
209231

210232
django_db_use_migrations
211233
""""""""""""""""""""""""

pytest_django/fixtures.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,35 +33,35 @@
3333

3434

3535
@pytest.fixture(scope="session")
36-
def django_db_modify_db_settings_xdist_suffix(request):
36+
def django_db_modify_db_settings_tox_suffix(request):
3737
skip_if_no_django()
3838

39-
from django.conf import settings
40-
41-
for db_settings in settings.DATABASES.values():
42-
43-
try:
44-
test_name = db_settings["TEST"]["NAME"]
45-
except KeyError:
46-
test_name = None
39+
tox_environment = os.getenv("TOX_PARALLEL_ENV")
40+
if tox_environment:
41+
# Put a suffix like _py27-django21 on tox workers
42+
_set_suffix_to_test_databases(suffix=tox_environment)
4743

48-
if not test_name:
49-
if db_settings["ENGINE"] == "django.db.backends.sqlite3":
50-
continue
5144

52-
test_name = "test_{}".format(db_settings["NAME"])
45+
@pytest.fixture(scope="session")
46+
def django_db_modify_db_settings_xdist_suffix(request):
47+
skip_if_no_django()
5348

49+
xdist_suffix = getattr(request.config, "slaveinput", {}).get("slaveid")
50+
if xdist_suffix:
5451
# Put a suffix like _gw0, _gw1 etc on xdist processes
55-
xdist_suffix = getattr(request.config, "slaveinput", {}).get("slaveid")
56-
if test_name != ":memory:" and xdist_suffix is not None:
57-
test_name = "{}_{}".format(test_name, xdist_suffix)
52+
_set_suffix_to_test_databases(suffix=xdist_suffix)
5853

59-
db_settings.setdefault("TEST", {})
60-
db_settings["TEST"]["NAME"] = test_name
54+
55+
@pytest.fixture(scope="session")
56+
def django_db_modify_db_settings_parallel_suffix(
57+
django_db_modify_db_settings_tox_suffix,
58+
django_db_modify_db_settings_xdist_suffix,
59+
):
60+
skip_if_no_django()
6161

6262

6363
@pytest.fixture(scope="session")
64-
def django_db_modify_db_settings(django_db_modify_db_settings_xdist_suffix):
64+
def django_db_modify_db_settings(django_db_modify_db_settings_parallel_suffix):
6565
skip_if_no_django()
6666

6767

@@ -169,6 +169,24 @@ def handle(self, *args, **kwargs):
169169
migrate.Command = MigrateSilentCommand
170170

171171

172+
def _set_suffix_to_test_databases(suffix):
173+
from django.conf import settings
174+
175+
for db_settings in settings.DATABASES.values():
176+
test_name = db_settings.get("TEST", {}).get("NAME")
177+
178+
if not test_name:
179+
if db_settings["ENGINE"] == "django.db.backends.sqlite3":
180+
continue
181+
test_name = "test_{}".format(db_settings["NAME"])
182+
183+
if test_name == ":memory:":
184+
continue
185+
186+
db_settings.setdefault("TEST", {})
187+
db_settings["TEST"]["NAME"] = "{}_{}".format(test_name, suffix)
188+
189+
172190
# ############### User visible fixtures ################
173191

174192

pytest_django/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
from .fixtures import django_db_keepdb # noqa
2323
from .fixtures import django_db_createdb # noqa
2424
from .fixtures import django_db_modify_db_settings # noqa
25+
from .fixtures import django_db_modify_db_settings_parallel_suffix # noqa
26+
from .fixtures import django_db_modify_db_settings_tox_suffix # noqa
2527
from .fixtures import django_db_modify_db_settings_xdist_suffix # noqa
2628
from .fixtures import _live_server_helper # noqa
2729
from .fixtures import admin_client # noqa

tests/test_db_setup.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,135 @@ def test_a():
288288
289289
assert conn_db2.vendor == 'sqlite'
290290
db_name = conn_db2.creation._get_test_db_name()
291-
assert 'test_custom_db_name_gw' in db_name
291+
assert db_name.startswith('test_custom_db_name_gw')
292+
"""
293+
)
294+
295+
result = django_testdir.runpytest_subprocess("--tb=short", "-vv", "-n1")
296+
assert result.ret == 0
297+
result.stdout.fnmatch_lines(["*PASSED*test_a*"])
298+
299+
300+
class TestSqliteWithTox:
301+
302+
db_settings = {
303+
"default": {
304+
"ENGINE": "django.db.backends.sqlite3",
305+
"NAME": "db_name",
306+
"TEST": {"NAME": "test_custom_db_name"},
307+
}
308+
}
309+
310+
def test_db_with_tox_suffix(self, django_testdir, monkeypatch):
311+
"A test to check that Tox DB suffix works when running in parallel."
312+
monkeypatch.setenv("TOX_PARALLEL_ENV", "py37-django22")
313+
314+
django_testdir.create_test_module(
315+
"""
316+
import pytest
317+
from django.db import connections
318+
319+
@pytest.mark.django_db
320+
def test_inner():
321+
322+
(conn, ) = connections.all()
323+
324+
assert conn.vendor == 'sqlite'
325+
db_name = conn.creation._get_test_db_name()
326+
assert db_name == 'test_custom_db_name_py37-django22'
327+
"""
328+
)
329+
330+
result = django_testdir.runpytest_subprocess("--tb=short", "-vv")
331+
assert result.ret == 0
332+
result.stdout.fnmatch_lines(["*test_inner*PASSED*"])
333+
334+
def test_db_with_empty_tox_suffix(self, django_testdir, monkeypatch):
335+
"A test to check that Tox DB suffix is not used when suffix would be empty."
336+
monkeypatch.setenv("TOX_PARALLEL_ENV", "")
337+
338+
django_testdir.create_test_module(
339+
"""
340+
import pytest
341+
from django.db import connections
342+
343+
@pytest.mark.django_db
344+
def test_inner():
345+
346+
(conn,) = connections.all()
347+
348+
assert conn.vendor == 'sqlite'
349+
db_name = conn.creation._get_test_db_name()
350+
assert db_name == 'test_custom_db_name'
351+
"""
352+
)
353+
354+
result = django_testdir.runpytest_subprocess("--tb=short", "-vv")
355+
assert result.ret == 0
356+
result.stdout.fnmatch_lines(["*test_inner*PASSED*"])
357+
358+
359+
class TestSqliteWithToxAndXdist:
360+
361+
db_settings = {
362+
"default": {
363+
"ENGINE": "django.db.backends.sqlite3",
364+
"NAME": "db_name",
365+
"TEST": {"NAME": "test_custom_db_name"},
366+
}
367+
}
368+
369+
def test_db_with_tox_suffix(self, django_testdir, monkeypatch):
370+
"A test to check that both Tox and xdist suffixes work together."
371+
pytest.importorskip("xdist")
372+
monkeypatch.setenv("TOX_PARALLEL_ENV", "py37-django22")
373+
374+
django_testdir.create_test_module(
375+
"""
376+
import pytest
377+
from django.db import connections
378+
379+
@pytest.mark.django_db
380+
def test_inner():
381+
382+
(conn, ) = connections.all()
383+
384+
assert conn.vendor == 'sqlite'
385+
db_name = conn.creation._get_test_db_name()
386+
assert db_name.startswith('test_custom_db_name_py37-django22_gw')
387+
"""
388+
)
389+
390+
result = django_testdir.runpytest_subprocess("--tb=short", "-vv", "-n1")
391+
assert result.ret == 0
392+
result.stdout.fnmatch_lines(["*PASSED*test_inner*"])
393+
394+
395+
class TestSqliteInMemoryWithXdist:
396+
397+
db_settings = {
398+
"default": {
399+
"ENGINE": "django.db.backends.sqlite3",
400+
"NAME": ":memory:",
401+
"TEST": {"NAME": ":memory:"},
402+
}
403+
}
404+
405+
def test_sqlite_in_memory_used(self, django_testdir):
406+
pytest.importorskip("xdist")
407+
408+
django_testdir.create_test_module(
409+
"""
410+
import pytest
411+
from django.db import connections
412+
413+
@pytest.mark.django_db
414+
def test_a():
415+
(conn, ) = connections.all()
416+
417+
assert conn.vendor == 'sqlite'
418+
db_name = conn.creation._get_test_db_name()
419+
assert db_name == ':memory:'
292420
"""
293421
)
294422

0 commit comments

Comments
 (0)