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

Pytests overhaul #569

Merged
merged 16 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ You can override the location of the binary using `DRAGONFLY_PATH` environment v
### Custom arguments

- use `--gdb` to start all instances inside gdb.
- use `--df arg=val` to pass custom arguments to all dragonfly instances. Can be used multiple times.
- use `--log-seeder file` to store all single-db commands from the lastest tests seeder inside file.

### Before you start
Please make sure that you have python 3 installed on you local host.
Expand Down
57 changes: 36 additions & 21 deletions tests/dragonfly/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import pytest
import typing
import time
import subprocess

import time
import subprocess

from dataclasses import dataclass

START_DELAY = 0.4
START_GDB_DELAY = 3.0


@dataclass
class DflyParams:
path: str
cwd: str
gdb: bool
args: list
env: any


Expand All @@ -29,24 +29,13 @@ def __init__(self, params: DflyParams, args):
self.proc = None

def start(self):
arglist = DflyInstance.format_args(self.args)

print(f"Starting instance on {self.port} with arguments {arglist}")

args = [self.params.path, *arglist]
if self.params.gdb:
args = ["gdb", "--ex", "r", "--args"] + args

self.proc = subprocess.Popen(args, cwd=self.params.cwd)
self._start()

# Give Dragonfly time to start and detect possible failure causes
# Gdb starts slowly
time.sleep(0.4 if not self.params.gdb else 3.0)
time.sleep(START_DELAY if not self.params.gdb else START_GDB_DELAY)

return_code = self.proc.poll()
if return_code is not None:
raise Exception(
f"Failed to start instance, return code {return_code}")
self._check_status()

def stop(self, kill=False):
proc, self.proc = self.proc, None
Expand All @@ -59,11 +48,26 @@ def stop(self, kill=False):
proc.kill()
else:
proc.terminate()
outs, errs = proc.communicate(timeout=15)
proc.communicate(timeout=15)
except subprocess.TimeoutExpired:
print("Unable to terminate DragonflyDB gracefully, it was killed")
outs, errs = proc.communicate()
print(outs, errs)
proc.kill()

def _start(self):
base_args = [f"--{v}" for v in self.params.args]
all_args = self.format_args(self.args) + base_args
print(f"Starting instance on {self.port} with arguments {all_args}")

run_cmd = [self.params.path, *all_args]
if self.params.gdb:
run_cmd = ["gdb", "--ex", "r", "--args"] + run_cmd
self.proc = subprocess.Popen(run_cmd, cwd=self.params.cwd)

def _check_status(self):
return_code = self.proc.poll()
if return_code is not None:
raise Exception(
f"Failed to start instance, return code {return_code}")

def __getitem__(self, k):
return self.args.get(k)
Expand Down Expand Up @@ -99,6 +103,17 @@ def create(self, **kwargs) -> DflyInstance:
self.instances.append(instance)
return instance

def start_all(self, instances):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use python test containers and not run them as sub processes (if I understand correctly, this is what it does here, spins DFs sub processes). They reject my PR for supporting DF, but you can start a DF container with the right parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inconvenient (increased memory requirements) and requires re-building a container just for testing some change

""" Start multiple instances in parallel """
for instance in instances:
instance._start()

delay = START_DELAY if not self.params.gdb else START_GDB_DELAY
time.sleep(delay * (1 + len(instances) / 2))

for instance in instances:
instance._check_status()

def stop_all(self):
"""Stop all lanched instances."""
for instance in self.instances:
Expand Down
29 changes: 23 additions & 6 deletions tests/dragonfly/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from tempfile import TemporaryDirectory

from . import DflyInstance, DflyInstanceFactory, DflyParams
from .utility import DflySeederFactory

DATABASE_INDEX = 1

Expand All @@ -39,12 +40,9 @@ def test_env(tmp_dir: Path):
env["DRAGONFLY_TMP"] = str(tmp_dir)
return env


def pytest_addoption(parser):
parser.addoption(
'--gdb', action='store_true', default=False, help='Run instances in gdb'
)

@pytest.fixture(scope="session", params=[{}])
def df_seeder_factory(request) -> DflySeederFactory:
return DflySeederFactory(request.config.getoption("--log-seeder"))

@pytest.fixture(scope="session", params=[{}])
def df_factory(request, tmp_dir, test_env) -> DflyInstanceFactory:
Expand All @@ -61,6 +59,7 @@ def df_factory(request, tmp_dir, test_env) -> DflyInstanceFactory:
path=path,
cwd=tmp_dir,
gdb=request.config.getoption("--gdb"),
args=request.config.getoption("--df"),
env=test_env
)

Expand Down Expand Up @@ -136,3 +135,21 @@ async def async_client(async_pool):
client = aioredis.Redis(connection_pool=async_pool)
await client.flushall()
return client


def pytest_addoption(parser):
"""
Custom pytest options:
--gdb - start all instances inside gdb
--df arg - pass arg to all instances, can be used multiple times
--log-seeder file - to log commands of last seeder run
"""
parser.addoption(
'--gdb', action='store_true', default=False, help='Run instances in gdb'
)
parser.addoption(
'--df', action='append', default=[], help='Add arguments to dragonfly'
)
parser.addoption(
'--log-seeder', action='store', default=None, help='Store last generator commands in file'
)
1 change: 1 addition & 0 deletions tests/dragonfly/json_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import redis
from redis.commands.json.path import Path
from .utility import *

Expand Down
Loading