Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabled parallel tests #757

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,13 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox tox-gh-actions
python -m pip install --upgrade tox-uv tox-gh-actions

- name: Docker compose up
run: docker compose -f docker/docker-compose.yml up -d --wait

- name: Tox tests
run: |
REDIS_PRIMARY=$(tests/start_redis.sh)
REDIS_SENTINEL=$(tests/start_redis.sh --sentinel)
CONTAINERS="$REDIS_PRIMARY $REDIS_SENTINEL"
trap "docker stop $CONTAINERS && docker rm $CONTAINERS" EXIT
tests/wait_for_redis.sh $REDIS_PRIMARY 6379
tests/wait_for_redis.sh $REDIS_SENTINEL 26379

tox
run: tox
env:
DJANGO: ${{ matrix.django-version }}
REDIS: ${{ matrix.redis-version }}
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/757.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Speed up tests by using `pytest-xdist` and separating settings on different redis databases.
Dropped `pytest-django`
Using `docker-compose` for setting up redis containers for testing
Use `tox-uv`
30 changes: 30 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:

redis:
image: redis:latest
container_name: redis-primary
command: redis-server --enable-debug-command yes --protected-mode no
ports:
- 6379:6379
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 5s
retries: 5

sentinel:
image: redis:latest
container_name: redis-sentinel
depends_on:
redis:
condition: service_healthy
entrypoint: "redis-sentinel /redis.conf --port 26379"
ports:
- 26379:26379
volumes:
- "./sentinel.conf:/redis.conf"
healthcheck:
test: redis-cli -p 26379 ping
interval: 5s
timeout: 5s
retries: 5
4 changes: 4 additions & 0 deletions docker/sentinel.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sentinel monitor default_service 127.0.0.1 6379 1
sentinel down-after-milliseconds default_service 3200
sentinel failover-timeout default_service 10000
sentinel parallel-syncs default_service 1
32 changes: 7 additions & 25 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,7 @@ REDIS =
[testenv]
passenv = CI, GITHUB*
commands =
{envpython} -m pytest --cov-report= --ds=settings.sqlite {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_herd {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_json {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_lz4 {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_msgpack {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sentinel {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sentinel_opts {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sharding {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_usock {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_zlib {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_zstd {posargs}
{envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_gzip {posargs}
{envpython} -m coverage report
{envpython} -m coverage xml
{envpython} -m pytest -n 4 {posargs}

deps =
dj42: Django>=4.2,<5.0
Expand All @@ -108,9 +95,9 @@ deps =
msgpack>=0.6.0
pytest
pytest-cov
pytest-django
pytest-pythonpath
pytest-mock
pytest-pythonpath
pytest-xdist
redismaster: https://github.com/redis/redis-py/archive/master.tar.gz
lz4>=0.15
pyzstd>=0.15
Expand All @@ -119,7 +106,7 @@ deps =
basepython = python3
envdir={toxworkdir}/lint
commands =
black: black --target-version py36 {posargs:--check --diff} setup.py django_redis/ tests/
black: black --target-version py38 {posargs:--check --diff} setup.py django_redis/ tests/
ruff: ruff {posargs:check --show-fixes} django_redis/ tests/
mypy: mypy {posargs:--cobertura-xml-report .} django_redis tests
deps =
Expand All @@ -130,24 +117,16 @@ deps =
mypy
# typing dependencies
pytest
pytest-django
pytest-mock
types-redis
skip_install = true

[tool:pytest]
DJANGO_SETTINGS_MODULE = settings.sqlite

addopts =
--doctest-modules
--cov=django_redis
--cov-config=setup.cfg
--no-cov-on-fail
filterwarnings =
error::DeprecationWarning
error::FutureWarning
error::PendingDeprecationWarning
ignore:.*distutils package is deprecated.*:DeprecationWarning
pythonpath = tests
testpaths = tests
xfail_strict = true
Expand All @@ -171,6 +150,9 @@ ignore_missing_settings = true
[mypy-lz4.frame]
ignore_missing_imports = true

[mypy-xdist.scheduler]
ignore_missing_imports = true

[mypy-pyzstd]
ignore_missing_imports = true

Expand Down
15 changes: 1 addition & 14 deletions tests/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,4 @@ Running the test suite
.. code-block:: bash

# start redis and a sentinel (uses docker with image redis:latest)
PRIMARY=$(tests/start_redis.sh)
SENTINEL=$(tests/start_redis.sh --sentinel)

# or just wait 5 - 10 seconds and most likely this would be the case
tests/wait_for_redis.sh $PRIMARY 6379
tests/wait_for_redis.sh $SENTINEL 26379

# run the tests
tox

# shut down redis
for container in $PRIMARY $SENTINEL; do
docker stop $container && docker rm $container
done
docker compose -f docker/docker-compose.yml up -d --wait
60 changes: 58 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,68 @@
import sys
from os import environ
from pathlib import Path
from typing import Iterable

import pytest
from django.core.cache import cache as default_cache
from xdist.scheduler import LoadScopeScheduling

Check warning on line 7 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L7

Added line #L7 was not covered by tests

from django_redis.cache import BaseCache
from tests.settings_wrapper import SettingsWrapper


class FixtureScheduling(LoadScopeScheduling):
"""Split by [] value. This is very hackish and might blow up any time!"""

def _split_scope(self, nodeid):
if "[sqlite" in nodeid:
return nodeid.rsplit("[")[-1].replace("]", "")
return None

Check warning on line 19 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L16-L19

Added lines #L16 - L19 were not covered by tests


def pytest_xdist_make_scheduler(log, config):
return FixtureScheduling(config, log)

Check warning on line 23 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L22-L23

Added lines #L22 - L23 were not covered by tests


def pytest_configure(config):
sys.path.insert(0, str(Path(__file__).absolute().parent))

Check warning on line 27 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L26-L27

Added lines #L26 - L27 were not covered by tests


@pytest.fixture()
def settings():

Check warning on line 31 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L31

Added line #L31 was not covered by tests
"""A Django settings object which restores changes after the testrun"""
wrapper = SettingsWrapper()
yield wrapper
wrapper.finalize()

Check warning on line 35 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L33-L35

Added lines #L33 - L35 were not covered by tests


@pytest.fixture
def cache() -> Iterable[BaseCache]:
def cache(cache_settings: str) -> Iterable[BaseCache]:
from django import setup

environ["DJANGO_SETTINGS_MODULE"] = f"settings.{cache_settings}"
setup()

from django.core.cache import cache as default_cache

yield default_cache
default_cache.clear()


def pytest_generate_tests(metafunc):
if "cache" in metafunc.fixturenames or "session" in metafunc.fixturenames:

Check warning on line 52 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L51-L52

Added lines #L51 - L52 were not covered by tests
# Mark
settings = [
"sqlite",
"sqlite_gzip",
"sqlite_herd",
"sqlite_json",
"sqlite_lz4",
"sqlite_msgpack",
"sqlite_sentinel",
"sqlite_sentinel_opts",
"sqlite_sharding",
"sqlite_usock",
"sqlite_zlib",
"sqlite_zstd",

Check warning on line 66 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L54-L66

Added lines #L54 - L66 were not covered by tests
]
metafunc.parametrize("cache_settings", settings)

Check warning on line 68 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L68

Added line #L68 was not covered by tests
8 changes: 4 additions & 4 deletions tests/settings/sqlite_gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,31 @@
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": ["redis://127.0.0.1:6379?db=1", "redis://127.0.0.1:6379?db=1"],
"LOCATION": ["redis://127.0.0.1:6379?db=2", "redis://127.0.0.1:6379?db=2"],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.gzip.GzipCompressor",
},
},
"doesnotexist": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:56379?db=1",
"LOCATION": "redis://127.0.0.1:56379?db=2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.gzip.GzipCompressor",
},
},
"sample": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1,redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=2,redis://127.0.0.1:6379?db=2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.gzip.GzipCompressor",
},
},
"with_prefix": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.gzip.GzipCompressor",
Expand Down
8 changes: 4 additions & 4 deletions tests/settings/sqlite_herd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": ["redis://127.0.0.1:6379?db=5"],
"LOCATION": ["redis://127.0.0.1:6379?db=3"],
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.HerdClient"},
},
"doesnotexist": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:56379?db=1",
"LOCATION": "redis://127.0.0.1:56379?db=3",
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.HerdClient"},
},
"sample": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1,redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=3,redis://127.0.0.1:6379?db=3",
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.HerdClient"},
},
"with_prefix": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=3",
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.HerdClient"},
"KEY_PREFIX": "test-prefix",
},
Expand Down
8 changes: 4 additions & 4 deletions tests/settings/sqlite_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,31 @@
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": ["redis://127.0.0.1:6379?db=1", "redis://127.0.0.1:6379?db=1"],
"LOCATION": ["redis://127.0.0.1:6379?db=4", "redis://127.0.0.1:6379?db=4"],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SERIALIZER": "django_redis.serializers.json.JSONSerializer",
},
},
"doesnotexist": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:56379?db=1",
"LOCATION": "redis://127.0.0.1:56379?db=4",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SERIALIZER": "django_redis.serializers.json.JSONSerializer",
},
},
"sample": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1,redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=4,redis://127.0.0.1:6379?db=4",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SERIALIZER": "django_redis.serializers.json.JSONSerializer",
},
},
"with_prefix": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=4",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SERIALIZER": "django_redis.serializers.json.JSONSerializer",
Expand Down
8 changes: 4 additions & 4 deletions tests/settings/sqlite_lz4.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,31 @@
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": ["redis://127.0.0.1:6379?db=1", "redis://127.0.0.1:6379?db=1"],
"LOCATION": ["redis://127.0.0.1:6379?db=5", "redis://127.0.0.1:6379?db=5"],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.lz4.Lz4Compressor",
},
},
"doesnotexist": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:56379?db=1",
"LOCATION": "redis://127.0.0.1:56379?db=5",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.lz4.Lz4Compressor",
},
},
"sample": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "127.0.0.1:6379?db=1,127.0.0.1:6379?db=1",
"LOCATION": "127.0.0.1:6379?db=5,127.0.0.1:6379?db=5",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.lz4.Lz4Compressor",
},
},
"with_prefix": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379?db=1",
"LOCATION": "redis://127.0.0.1:6379?db=5",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.lz4.Lz4Compressor",
Expand Down
Loading
Loading